﻿///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2023, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда

#Область ПрограммныйИнтерфейс

#Область ДляВызоваИзДругихПодсистем

// СтандартныеПодсистемы.ГрупповоеИзменениеОбъектов

// Возвращает реквизиты объекта, которые не рекомендуется редактировать
// с помощью обработки группового изменения реквизитов.
//
// Возвращаемое значение:
//  Массив из Строка
//
Функция РеквизитыНеРедактируемыеВГрупповойОбработке() Экспорт
	
	НеРедактируемыеРеквизиты = Новый Массив;
	НеРедактируемыеРеквизиты.Добавить("ИдентификаторПоставляемыхДанных");
	НеРедактируемыеРеквизиты.Добавить("ПоставляемыйПрофильИзменен");
	НеРедактируемыеРеквизиты.Добавить("ВидыДоступа.*");
	НеРедактируемыеРеквизиты.Добавить("ЗначенияДоступа.*");
	
	Возврат НеРедактируемыеРеквизиты;
	
КонецФункции

// Конец СтандартныеПодсистемы.ГрупповоеИзменениеОбъектов

// СтандартныеПодсистемы.УправлениеДоступом

// Параметры:
//   Ограничение - см. УправлениеДоступомПереопределяемый.ПриЗаполненииОграниченияДоступа.Ограничение.
//
Процедура ПриЗаполненииОграниченияДоступа(Ограничение) Экспорт
	
	Ограничение.Текст =
	"ПрисоединитьДополнительныеТаблицы
	|ЭтотСписок КАК ПрофилиГруппДоступа
	|
	|ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ГруппыДоступа КАК ГруппыДоступа
	|	ПО ГруппыДоступа.Профиль = ПрофилиГруппДоступа.Ссылка
	|;
	|РазрешитьЧтениеИзменение
	|ГДЕ
	|	ЭтоГруппа
	|	ИЛИ Ссылка <> Значение(Справочник.ПрофилиГруппДоступа.Администратор)
	|	  И ЭтоАвторизованныйПользователь(ГруппыДоступа.Ответственный)";
	
КонецПроцедуры

// Конец СтандартныеПодсистемы.УправлениеДоступом

// ТехнологияСервиса.ВыгрузкаЗагрузкаДанных

// Подключается в ВыгрузкаЗагрузкаДанныхПереопределяемый.ПриРегистрацииОбработчиковВыгрузкиДанных.
//
// Параметры:
//   Контейнер - ОбработкаОбъект.ВыгрузкаЗагрузкаДанныхМенеджерКонтейнера
//   МенеджерВыгрузкиОбъекта - ОбработкаОбъект.ВыгрузкаЗагрузкаДанныхМенеджерВыгрузкиДанныхИнформационнойБазы
//   Сериализатор - СериализаторXDTO
//   Объект - КонстантаМенеджерЗначения
//          - СправочникОбъект
//          - ДокументОбъект
//          - БизнесПроцессОбъект
//          - ЗадачаОбъект
//          - ПланСчетовОбъект
//          - ПланОбменаОбъект
//          - ПланВидовХарактеристикОбъект
//          - ПланВидовРасчетаОбъект
//          - РегистрСведенийНаборЗаписей
//          - РегистрНакопленияНаборЗаписей
//          - РегистрБухгалтерииНаборЗаписей
//          - РегистрРасчетаНаборЗаписей
//          - ПоследовательностьНаборЗаписей
//          - ПерерасчетНаборЗаписей
//   Артефакты - Массив из ОбъектXDTO
//   Отказ - Булево
//
Процедура ПередВыгрузкойОбъекта(Контейнер, МенеджерВыгрузкиОбъекта, Сериализатор, Объект, Артефакты, Отказ) Экспорт
	
	УправлениеДоступомСлужебный.ПередВыгрузкойОбъекта(Контейнер, МенеджерВыгрузкиОбъекта, Сериализатор, Объект, Артефакты, Отказ);
	
КонецПроцедуры

// Конец ТехнологияСервиса.ВыгрузкаЗагрузкаДанных

#КонецОбласти

#КонецОбласти

#Область СлужебныйПрограммныйИнтерфейс

// Процедура Обновляет описание поставляемых профилей в
// параметрах ограничения доступа при изменении конфигурации.
//
// Параметры:
//  ЕстьИзменения - Булево - возвращаемое значение. Если производилась запись,
//                  устанавливается Истина, иначе не изменяется.
//
Процедура ОбновитьОписаниеПоставляемыхПрофилей(ЕстьИзменения = Неопределено) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	СвойстваСеанса = УправлениеДоступомСлужебныйПовтИсп.ОписаниеСвойствВидовДоступаСеанса().СвойстваСеанса;
	НовоеЗначение = "";
	ПроверенныеПоставляемыеПрофилиСеанса(СвойстваСеанса, НовоеЗначение);
	
	НачатьТранзакцию();
	Попытка
		ЕстьТекущиеИзменения = Ложь;
		
		СтандартныеПодсистемыСервер.ОбновитьПараметрРаботыПрограммы(
			"СтандартныеПодсистемы.УправлениеДоступом.ОписаниеПоставляемыхПрофилей",
			НовоеЗначение, ЕстьТекущиеИзменения);
		
		СтандартныеПодсистемыСервер.ДобавитьИзмененияПараметраРаботыПрограммы(
			"СтандартныеПодсистемы.УправлениеДоступом.ОписаниеПоставляемыхПрофилей",
			?(ЕстьТекущиеИзменения,
			  Новый ФиксированнаяСтруктура("ЕстьИзменения", Истина),
			  Новый ФиксированнаяСтруктура()) );
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	Если ЕстьТекущиеИзменения Тогда
		ЕстьИзменения = Истина;
	КонецЕсли;
	
КонецПроцедуры

// Процедура Обновляет состав предопределенных профилей в
// параметрах ограничения доступа при изменении конфигурации.
//
// Параметры:
//  ЕстьИзменения - Булево - возвращаемое значение. Если производилась запись,
//                  устанавливается Истина, иначе не изменяется.
//
Процедура ОбновитьСоставПредопределенныхПрофилей(ЕстьИзменения = Неопределено) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	ПредопределенныеПрофили = Метаданные.Справочники.ПрофилиГруппДоступа.ПолучитьИменаПредопределенных();
	
	НачатьТранзакцию();
	Попытка
		Удаленные = Новый Массив;
		ЕстьТекущиеИзменения = Ложь;
		СтароеЗначение = Неопределено;
		
		СтандартныеПодсистемыСервер.ОбновитьПараметрРаботыПрограммы(
			"СтандартныеПодсистемы.УправлениеДоступом.ПредопределенныеПрофилиГруппДоступа",
			ПредопределенныеПрофили, , СтароеЗначение);
		
		Если Не ПредопределенныеПрофилиСовпадают(ПредопределенныеПрофили, СтароеЗначение, Удаленные) Тогда
			ЕстьТекущиеИзменения = Истина;
		КонецЕсли;
		
		СтандартныеПодсистемыСервер.ДобавитьИзмененияПараметраРаботыПрограммы(
			"СтандартныеПодсистемы.УправлениеДоступом.ПредопределенныеПрофилиГруппДоступа",
			?(ЗначениеЗаполнено(Удаленные),
			  Новый ФиксированнаяСтруктура("Удаленные", Новый ФиксированныйМассив(Удаленные)),
			  Новый ФиксированнаяСтруктура()) );
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	Если ЕстьТекущиеИзменения Тогда
		ЕстьИзменения = Истина;
	КонецЕсли;
	
КонецПроцедуры

// Процедура обновляет поставляемые профили справочника по результату изменения
// хеш-суммы описаний поставляемых профилей, сохраненной в параметрах ограничения доступа.
//
Процедура ОбновитьПоставляемыеПрофилиПоИзменениямКонфигурации() Экспорт
	
	Кэш = УправлениеДоступомСлужебныйПовтИсп.ОписаниеПоставляемыхПрофилейСеанса();
	НовоеЗначение = Кэш.ХешСумма;
	
	УжеИзменен = Ложь;
	ИмяПараметра = "СтандартныеПодсистемы.УправлениеДоступом.ОбновленныеПоставляемыеПрофили";
	СтароеЗначение = СтандартныеПодсистемыСервер.ПараметрРаботыРасширения(ИмяПараметра, Истина, УжеИзменен);
	
	Если СтароеЗначение = НовоеЗначение Тогда
		Возврат;
	КонецЕсли;
	
	Удаленные = Новый Массив;
	
	Если ОбновлениеИнформационнойБазы.ВыполняетсяОбновлениеИнформационнойБазы() Тогда
		ПоследниеИзменения = СтандартныеПодсистемыСервер.ИзмененияПараметраРаботыПрограммы(
			"СтандартныеПодсистемы.УправлениеДоступом.ПредопределенныеПрофилиГруппДоступа");
		
		Если ПоследниеИзменения <> Неопределено Тогда
			Для Каждого ЧастьИзменений Из ПоследниеИзменения Цикл
				Если ТипЗнч(ЧастьИзменений) = Тип("ФиксированнаяСтруктура")
				   И ЧастьИзменений.Свойство("Удаленные")
				   И ТипЗнч(ЧастьИзменений.Удаленные) = Тип("ФиксированныйМассив") Тогда
					
					Для Каждого Удаленный Из ЧастьИзменений.Удаленные Цикл
						Удаленные.Добавить(Удаленный);
					КонецЦикла;
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
	КонецЕсли;
	
	Если УжеИзменен Тогда
		УправлениеДоступомСлужебный.ПроверитьАктуальностьМетаданных();
	КонецЕсли;
	
	ОбновитьПоставляемыеПрофили(, Удаленные);
	СтандартныеПодсистемыСервер.УстановитьПараметрРаботыРасширения(ИмяПараметра, НовоеЗначение, Истина);
	
КонецПроцедуры

// Процедура обновляет непоставляемые профили справочника по результату изменения
// хеш-суммы стандартных ролей расширений, сохраненной в параметрах ограничения доступа.
//
Процедура ОбновитьНепоставляемыеПрофилиПоИзменениямКонфигурации() Экспорт
	
	Кэш = УправлениеДоступомСлужебныйПовтИсп.ОписаниеПоставляемыхПрофилейСеанса();
	НовоеЗначение = Кэш.ХешСумма;
	
	УжеИзменен = Ложь;
	ИмяПараметра = "СтандартныеПодсистемы.УправлениеДоступом.ОбновленныеНепоставляемыеПрофили";
	СтароеЗначение = СтандартныеПодсистемыСервер.ПараметрРаботыРасширения(ИмяПараметра, Истина, УжеИзменен);
	
	Если СтароеЗначение = НовоеЗначение Тогда
		Возврат;
	КонецЕсли;
	
	Если УжеИзменен Тогда
		УправлениеДоступомСлужебный.ПроверитьАктуальностьМетаданных();
	КонецЕсли;
	
	ОбновитьНепоставляемыеПрофили();
	СтандартныеПодсистемыСервер.УстановитьПараметрРаботыРасширения(ИмяПараметра, НовоеЗначение, Истина);
	
КонецПроцедуры

// Обновляет поставляемые папки профилей и профили.
// При необходимости обновляет группы доступа обновленных профилей.
// Создаются не найденные поставляемые папки профилей и профили групп доступа.
//
// Особенности обновления настраиваются в процедуре ПриЗаполненииПоставляемыхПрофилейГруппДоступа
// общего модуля УправлениеДоступомПереопределяемый (см. комментарий к процедуре).
//
// Параметры:
//  ЕстьИзменения - Булево - возвращаемое значение. Если производилась запись,
//                  устанавливается Истина, иначе не изменяется.
//  Удаленные     - Массив - имена предопределенных элементов, которые были удалены,
//                  используются для снятия пометки удаления с поставляемых данных с теми же именами.
//                - Неопределено
//
Процедура ОбновитьПоставляемыеПрофили(ЕстьИзменения = Неопределено, Удаленные = Неопределено) Экспорт
	
	// Кэширование идентификаторов всех ролей сразу для сокращения количества обращений к базе данных,
	// так как идентификаторы почти всех ролей будут получены при обновлении всех поставляемых профилей.
	ВсеРоли = Новый Массив;
	Для Каждого Роль Из Метаданные.Роли Цикл
		ВсеРоли.Добавить(Роль);
	КонецЦикла;
	ОбщегоНазначения.ИдентификаторыОбъектовМетаданных(ВсеРоли);
	
	ПоставляемыеПрофили = УправлениеДоступомСлужебный.ПоставляемыеПрофили();
	
	ТекущиеПапкиПрофилей = ТекущиеПапкиПрофилей();
	ОбновитьПоставляемыеПапкиПрофилей("", ТекущиеПапкиПрофилей, ПоставляемыеПрофили, Удаленные, ЕстьИзменения);
	ПометитьНаУдалениеУстаревшиеПоставляемыеДанные(ТекущиеПапкиПрофилей, ЕстьИзменения);
	
	ТекущиеПрофили = ТекущиеПрофили();
	ОбновленныеПрофили  = Новый Массив;
	ОбновитьПоставляемыеПрофилиБезПапок(ОбновленныеПрофили,
		ТекущиеПрофили, ТекущиеПапкиПрофилей, ПоставляемыеПрофили, Удаленные, ЕстьИзменения);
	ПометитьНаУдалениеУстаревшиеПоставляемыеДанные(ТекущиеПрофили, ЕстьИзменения, ОбновленныеПрофили);
	ОбновитьВспомогательныеДанныеПрофилей(ОбновленныеПрофили, ЕстьИзменения);
	
КонецПроцедуры

// Обновляет непоставляемые основные профили.
//
// Параметры:
//  ЕстьИзменения - Булево - возвращаемое значение. Если производилась запись,
//                  устанавливается Истина, иначе не изменяется.
//
Процедура ОбновитьНепоставляемыеПрофили(ЕстьИзменения = Неопределено) Экспорт
	
	ОсновныеНепоставляемыеПрофили = ОсновныеНепоставляемыеПрофили();
	ОбновленныеПрофили = Новый Массив;
	
	Для Каждого ПрофильСсылка Из ОсновныеНепоставляемыеПрофили Цикл
		ПрофильОбъект = ПрофильСсылка.ПолучитьОбъект();
		
		ЗаполнитьСтандартныеРолиРасширений(ПрофильОбъект.Роли);
		Если Не ПрофильОбъект.Модифицированность() Тогда
			Продолжить;
		КонецЕсли;
		
		Если Не ОбновлениеИнформационнойБазы.ВыполняетсяОбновлениеИнформационнойБазы()
		   И Не ОбновлениеИнформационнойБазы.ЭтоВызовИзОбработчикаОбновления() Тогда
			
			ЗаблокироватьДанныеДляРедактирования(ПрофильОбъект.Ссылка, ПрофильОбъект.ВерсияДанных);
		КонецЕсли;
		
		ПрофильОбъект.ДополнительныеСвойства.Вставить("НеОбновлятьРолиПользователей");
		ОбновлениеИнформационнойБазы.ЗаписатьОбъект(ПрофильОбъект);
		
		ЕстьИзменения = Истина;
		ОбновленныеПрофили.Добавить(ПрофильСсылка);
		
		Если Не ОбновлениеИнформационнойБазы.ВыполняетсяОбновлениеИнформационнойБазы()
		   И Не ОбновлениеИнформационнойБазы.ЭтоВызовИзОбработчикаОбновления() Тогда
			
			РазблокироватьДанныеДляРедактирования(ПрофильОбъект.Ссылка);
		КонецЕсли;
	КонецЦикла;
	
	ОбновитьВспомогательныеДанныеПрофилей(ОбновленныеПрофили, ЕстьИзменения);
	
КонецПроцедуры

Процедура ОбновитьВспомогательныеДанныеПрофилей(Профили = Неопределено, ЕстьИзменения = Ложь) Экспорт
	
	Если Профили = Неопределено Тогда
		РегистрыСведений.ТаблицыГруппДоступа.ОбновитьДанныеРегистра( , , ЕстьИзменения);
		РегистрыСведений.ЗначенияГруппДоступа.ОбновитьДанныеРегистра( , ЕстьИзменения);
		УправлениеДоступомСлужебный.ОбновитьРолиПользователей( , , ЕстьИзменения);
		
	ИначеЕсли Профили.Количество() > 0 Тогда
		ГруппыДоступаПрофилей = Справочники.ГруппыДоступа.ГруппыДоступаПрофиля(Профили);
		РегистрыСведений.ТаблицыГруппДоступа.ОбновитьДанныеРегистра(ГруппыДоступаПрофилей, , ЕстьИзменения);
		РегистрыСведений.ЗначенияГруппДоступа.ОбновитьДанныеРегистра(ГруппыДоступаПрофилей, ЕстьИзменения);
		
		// Обновление ролей пользователей.
		ПользователиДляОбновления =
			Справочники.ГруппыДоступа.ПользователиДляОбновленияРолейПоПрофилю(Профили);
		
		УправлениеДоступомСлужебный.ОбновитьРолиПользователей(ПользователиДляОбновления, , ЕстьИзменения);
	КонецЕсли;
	
КонецПроцедуры

// Параметры:
//   ТекущиеДела - см. ТекущиеДелаСервер.ТекущиеДела.
//
Процедура ПриЗаполненииСпискаТекущихДел(ТекущиеДела) Экспорт
	
	МодульТекущиеДелаСервер = ОбщегоНазначения.ОбщийМодуль("ТекущиеДелаСервер");
	Если Не Пользователи.ЭтоПолноправныйПользователь()
		Или МодульТекущиеДелаСервер.ДелоОтключено("ПрофилиГруппДоступа") Тогда
		Возврат;
	КонецЕсли;
	
	// Процедура вызывается только при наличии подсистемы "Текущие дела", поэтому здесь
	// не делается проверка существования подсистемы.
	Разделы = МодульТекущиеДелаСервер.РазделыДляОбъекта(Метаданные.Справочники.ПрофилиГруппДоступа.ПолноеИмя());
	КоличествоНесовместимыхПрофилейГруппДоступа = НесовместимыеПрофилиГруппДоступа().Количество();
	
	Для Каждого Раздел Из Разделы Цикл
		
		ИдентификаторПрофиля = "НеСовместимыСТекущейВерсией" + СтрЗаменить(Раздел.ПолноеИмя(), ".", "");
		Дело = ТекущиеДела.Добавить();
		Дело.Идентификатор = ИдентификаторПрофиля;
		Дело.ЕстьДела      = КоличествоНесовместимыхПрофилейГруппДоступа > 0;
		Дело.Представление = НСтр("ru = 'Не совместимы с текущей версией'");
		Дело.Количество    = КоличествоНесовместимыхПрофилейГруппДоступа;
		Дело.Владелец      = Раздел;
		
		Дело = ТекущиеДела.Добавить();
		Дело.Идентификатор = "ПрофилиГруппДоступа";
		Дело.ЕстьДела      = КоличествоНесовместимыхПрофилейГруппДоступа > 0;
		Дело.Важное        = Истина;
		Дело.Представление = НСтр("ru = 'Профили групп доступа'");
		Дело.Количество    = КоличествоНесовместимыхПрофилейГруппДоступа;
		Дело.Форма         = "Справочник.ПрофилиГруппДоступа.ФормаСписка";
		Дело.ПараметрыФормы= Новый Структура("ПрофилиСРолямиПомеченнымиНаУдаление", Истина);
		Дело.Владелец      = ИдентификаторПрофиля;
		
	КонецЦикла;
	
КонецПроцедуры

// Параметры:
//  РолиПрофиля - Массив из Строка - имена ролей.
//              - ТабличнаяЧасть:
//                 * Роль - СправочникСсылка.ИдентификаторыОбъектовМетаданных
//                        - СправочникСсылка.ИдентификаторыОбъектовРасширений
//              - ДанныеФормыКоллекция:
//                 * Роль - Строка - имя роли.
//
Процедура ЗаполнитьСтандартныеРолиРасширений(РолиПрофиля, СтандартныеРолиРасширений = Неопределено) Экспорт
	
	Если СтандартныеРолиРасширений = Неопределено Тогда
		СтандартныеРолиРасширений = УправлениеДоступомСлужебный.СтандартныеРолиРасширений();
	КонецЕсли;
	СтандартныеРолиПрофиля = СтандартныеРолиПрофиля(РолиПрофиля);
	
	// АдминистраторСистемы.
	УстановитьРолиВПрофиле(РолиПрофиля,
		СтандартныеРолиРасширений.АдминистраторСистемы,
		СтандартныеРолиПрофиля.АдминистраторСистемы);
	
	// ПолныеПрава.
	УстановитьРолиВПрофиле(РолиПрофиля,
		СтандартныеРолиРасширений.ПолныеПрава,
		СтандартныеРолиПрофиля.ПолныеПрава);
	
	// БазовыеПраваБСП.
	УстановитьРолиВПрофиле(РолиПрофиля,
		СтандартныеРолиРасширений.БазовыеПрава,
		СтандартныеРолиПрофиля.БазовыеПраваБСП);
	
	// БазовыеПраваВнешнихПользователейБСП.
	УстановитьРолиВПрофиле(РолиПрофиля,
		СтандартныеРолиРасширений.БазовыеПраваВнешнихПользователей,
		СтандартныеРолиПрофиля.БазовыеПраваВнешнихПользователейБСП);
	
	// Общие права.
	УстановитьРолиВПрофиле(РолиПрофиля,
		СтандартныеРолиРасширений.ОбщиеПрава,
		СтандартныеРолиПрофиля.ПолныеПрава
			Или СтандартныеРолиПрофиля.БазовыеПраваБСП
			Или СтандартныеРолиПрофиля.БазовыеПраваВнешнихПользователейБСП);
	
	// Очистка удаленных.
	ОчиститьУдаленныеСтандартныеРолиРасширений(РолиПрофиля);
	
КонецПроцедуры

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

// Возвращает строку уникального идентификатора
// поставляемого и предопределенного профиля Администратор.
//
// Возвращаемое значение:
//  Строка - строка уникального идентификатора.
//
Функция ИдентификаторПрофиляАдминистратор() Экспорт
	
	Возврат "6c4b0307-43a4-4141-9c35-3dd7e9586d41";
	
КонецФункции

// См. УправлениеДоступом.ПрофильАдминистратор
Функция ПрофильАдминистратор() Экспорт
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ПрофилиГруппДоступа.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.ПрофилиГруппДоступа КАК ПрофилиГруппДоступа
	|ГДЕ
	|	ПрофилиГруппДоступа.ИдентификаторПоставляемыхДанных = &ИдентификаторПоставляемыхДанных
	|
	|УПОРЯДОЧИТЬ ПО
	|	Ссылка
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ПрофилиГруппДоступа.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.ПрофилиГруппДоступа КАК ПрофилиГруппДоступа
	|ГДЕ
	|	ПрофилиГруппДоступа.ИмяПредопределенныхДанных = &ИмяПредопределенныхДанных
	|
	|УПОРЯДОЧИТЬ ПО
	|	Ссылка";
	
	Идентификатор = Новый УникальныйИдентификатор(ИдентификаторПрофиляАдминистратор());
	Запрос.УстановитьПараметр("ИдентификаторПоставляемыхДанных", Идентификатор);
	Запрос.УстановитьПараметр("ИмяПредопределенныхДанных", "Администратор");
	
	РезультатыЗапроса = Запрос.ВыполнитьПакет();
	ВыборкаПоИдентификатору    = РезультатыЗапроса[0].Выбрать();
	ВыборкаПоПредопределенному = РезультатыЗапроса[1].Выбрать();
	
	Если ВыборкаПоИдентификатору.Следующий()
	   И ВыборкаПоПредопределенному.Следующий()
	   И ВыборкаПоИдентификатору.Количество() = 1
	   И ВыборкаПоПредопределенному.Количество() = 1
	   И ВыборкаПоИдентификатору.Ссылка = ВыборкаПоПредопределенному.Ссылка Тогда
		
		Возврат ВыборкаПоИдентификатору.Ссылка;
	КонецЕсли;
	
	Блокировка = Новый БлокировкаДанных;
	Блокировка.Добавить("Справочник.ПрофилиГруппДоступа");
	Блокировка.Добавить("Справочник.ГруппыДоступа");
	
	ПоставляемыеПрофили = УправлениеДоступомСлужебный.ПоставляемыеПрофили();
	СвойстваПрофиляАдминистратор = ПоставляемыеПрофили.ОписанияПрофилей.Получить(
		ИдентификаторПрофиляАдминистратор());
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		РезультатыЗапроса = Запрос.ВыполнитьПакет();
		ВыборкаПоИдентификатору    = РезультатыЗапроса[0].Выбрать();
		ВыборкаПоПредопределенному = РезультатыЗапроса[1].Выбрать();
		Если ВыборкаПоИдентификатору.Следующий() Тогда
			ПрофильОбъект = ВыборкаПоИдентификатору.Ссылка.ПолучитьОбъект();
			Если ПрофильОбъект.ИмяПредопределенныхДанных <> "Администратор" Тогда
				ПрофильОбъект.ИмяПредопределенныхДанных = "Администратор";
			КонецЕсли;
		ИначеЕсли ВыборкаПоПредопределенному.Следующий() Тогда
			ПрофильОбъект = ВыборкаПоПредопределенному.Ссылка.ПолучитьОбъект();
			ПрофильОбъект.ИдентификаторПоставляемыхДанных = Идентификатор;
		Иначе
			ПрофильПоНаименованию = ПрофильПоНаименованию(
				НСтр("ru = 'Администратор'", ОбщегоНазначения.КодОсновногоЯзыка()));
			Если ЗначениеЗаполнено(ПрофильПоНаименованию) Тогда
				ПрофильОбъект = ПрофильПоНаименованию.ПолучитьОбъект();
			Иначе
				ПрофильОбъект = СоздатьЭлемент();
			КонецЕсли;
			ПрофильОбъект.ИдентификаторПоставляемыхДанных = Идентификатор;
			ПрофильОбъект.ИмяПредопределенныхДанных = "Администратор";
		КонецЕсли;
		Если ПрофильОбъект.Модифицированность() Тогда
			ОбновлениеИнформационнойБазы.ЗаписатьОбъект(ПрофильОбъект, Ложь, Ложь);
		КонецЕсли;
		
		ОбъектыДляОтвязки = Новый Соответствие;
		Пока ВыборкаПоИдентификатору.Следующий() Цикл
			Если ВыборкаПоИдентификатору.Ссылка <> ПрофильОбъект.Ссылка Тогда
				ОбъектыДляОтвязки.Вставить(ВыборкаПоИдентификатору.Ссылка);
			КонецЕсли;
		КонецЦикла;
		Пока ВыборкаПоПредопределенному.Следующий() Цикл
			Если ВыборкаПоПредопределенному.Ссылка <> ПрофильОбъект.Ссылка Тогда
				ОбъектыДляОтвязки.Вставить(ВыборкаПоПредопределенному.Ссылка);
			КонецЕсли;
		КонецЦикла;
		Для Каждого КлючИЗначение Из ОбъектыДляОтвязки Цикл
			ТекущийПрофильОбъект = КлючИЗначение.Ключ.ПолучитьОбъект();
			ТекущийПрофильОбъект.ИдентификаторПоставляемыхДанных = Неопределено;
			ТекущийПрофильОбъект.ИмяПредопределенныхДанных = "";
			ОбновлениеИнформационнойБазы.ЗаписатьОбъект(ТекущийПрофильОбъект, Ложь, Ложь);
		КонецЦикла;
		Для Каждого КлючИЗначение Из ОбъектыДляОтвязки Цикл
			ТекущийПрофильОбъект = КлючИЗначение.Ключ.ПолучитьОбъект();
			ОбновлениеИнформационнойБазы.ЗаписатьОбъект(ТекущийПрофильОбъект);
		КонецЦикла;
		
		ОбновитьПрофильИлиПапкуПрофилей(СвойстваПрофиляАдминистратор);
		Справочники.ГруппыДоступа.ГруппаДоступаАдминистраторы(ПрофильОбъект.Ссылка);
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	Возврат ПрофильОбъект.Ссылка;
	
КонецФункции

// Для функции ПрофильАдминистратор.
Функция ПрофильПоНаименованию(Наименование)
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("Наименование", Наименование);
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ПрофилиГруппДоступа.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.ПрофилиГруппДоступа КАК ПрофилиГруппДоступа
	|ГДЕ
	|	ПрофилиГруппДоступа.Наименование = &Наименование
	|
	|УПОРЯДОЧИТЬ ПО
	|	Ссылка";
	
	Выборка = Запрос.Выполнить().Выбрать();
	Если Выборка.Следующий() Тогда
		Возврат Выборка.Ссылка;
	КонецЕсли;
	
	Возврат Неопределено;
	
КонецФункции

// Возвращает ссылку на поставляемый профиль или папку профилей по идентификатору.
//
// Параметры:
//  Идентификатор - Строка - имя или уникальный идентификатор поставляемого профиля или папки профилей,
//                  как указано в процедуре ПриЗаполненииПоставляемыхПрофилейГруппДоступа
//                  общего модуля УправлениеДоступомПереопределяемый.
//
//  ВызыватьИсключениеЕслиНетВБазеДанных - Булево
//  БезПапок      - Булево
//
// Возвращаемое значение:
//  СправочникСсылка.ПрофилиГруппДоступа - если поставляемый профиль или папка профилей найдена в справочнике.
//  Неопределено - если поставляемый профиль или папка профилей не существует в справочнике.
//
Функция ПоставляемыйПрофильПоИдентификатору(Идентификатор, ВызыватьИсключениеЕслиНетВБазеДанных = Ложь, БезПапок = Ложь) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	ПоставляемыеПрофили = УправлениеДоступомСлужебный.ПоставляемыеПрофили();
	СвойстваПрофиля = ПоставляемыеПрофили.ОписанияПрофилей.Получить(Строка(Идентификатор)); // См. СвойстваПоставляемогоПрофиля
	
	Если СвойстваПрофиля = Неопределено Или СвойстваПрофиля.ЭтоГруппа И БезПапок Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Профиль c идентификатором ""%1""
			           |не поставляется в программе.'"),
			Строка(Идентификатор));
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ИдентификаторПоставляемыхДанных",
		Новый УникальныйИдентификатор(СвойстваПрофиля.Идентификатор));
	
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ПрофилиГруппДоступа.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.ПрофилиГруппДоступа КАК ПрофилиГруппДоступа
	|ГДЕ
	|	ПрофилиГруппДоступа.ИдентификаторПоставляемыхДанных = &ИдентификаторПоставляемыхДанных
	|
	|УПОРЯДОЧИТЬ ПО
	|	Ссылка";
	
	Выборка = Запрос.Выполнить().Выбрать();
	
	Если Выборка.Следующий() Тогда
		Возврат Выборка.Ссылка;
	КонецЕсли;
	
	Если ВызыватьИсключениеЕслиНетВБазеДанных Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Поставляемый профиль с идентификатором ""%1""
			           |не существует в программе.'"),
			Строка(Идентификатор));
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Возврат Неопределено;
	
КонецФункции

// Возвращает строку уникального идентификатора
// данных поставляемого профиля.
//
// Возвращаемое значение:
//  Строка
//  Неопределено
//
Функция ИдентификаторПоставляемогоПрофиля(Профиль) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("Ссылка", Профиль);
	
	Запрос.УстановитьПараметр("ПустойУникальныйИдентификатор",
		ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор());
	
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ПрофилиГруппДоступа.ИдентификаторПоставляемыхДанных
	|ИЗ
	|	Справочник.ПрофилиГруппДоступа КАК ПрофилиГруппДоступа
	|ГДЕ
	|	ПрофилиГруппДоступа.Ссылка = &Ссылка
	|	И ПрофилиГруппДоступа.ИдентификаторПоставляемыхДанных <> &ПустойУникальныйИдентификатор";
	
	Выборка = Запрос.Выполнить().Выбрать();
	Если Выборка.Следующий() Тогда
		Возврат Строка(Выборка.ИдентификаторПоставляемыхДанных);
	КонецЕсли;
	
	Возврат Неопределено;
	
КонецФункции

// Проверяет изменен ли поставляемый профиль или папка профилей по сравнению с описанием из процедуры
// УправлениеДоступомПереопределяемый.ПриЗаполненииПоставляемыхПрофилейГруппДоступа().
//
// Параметры:
//  Профиль      - СправочникСсылка.ПрофилиГруппДоступа
//                     (возвращается реквизит ПоставляемыйПрофильИзменен),
//               - СправочникОбъект.ПрофилиГруппДоступа
//                     (возвращается результат сравнения заполнения объекта
//                      с описанием в переопределяемом общем модуле).
//
// Возвращаемое значение:
//  Булево
//
Функция ПоставляемыйПрофильИзменен(Профиль) Экспорт
	
	Если ТипЗнч(Профиль) = Тип("СправочникСсылка.ПрофилиГруппДоступа") Тогда
		Возврат ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Профиль,
			"ПоставляемыйПрофильИзменен") = Истина;
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(Профиль.ИдентификаторПоставляемыхДанных) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ОписанияПрофилей = УправлениеДоступомСлужебный.ПоставляемыеПрофили().ОписанияПрофилей;
	СвойстваПрофиля = ОписанияПрофилей.Получить(Строка(Профиль.ИдентификаторПоставляемыхДанных)); // См. СвойстваПоставляемогоПрофиля
	
	Если СвойстваПрофиля = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ВРег(Профиль.Наименование) <> ВРег(СвойстваПрофиля.Наименование) Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(СвойстваПрофиля.Родитель) Тогда
		Родитель = ПоставляемыйПрофильПоИдентификатору(СвойстваПрофиля.Родитель);
		Если ЗначениеЗаполнено(Родитель) И Профиль.Родитель <> Родитель Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЕсли;
	
	Если СвойстваПрофиля.ЭтоГруппа Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ОписаниеРолейПрофиля = ОписаниеРолейПрофиля(СвойстваПрофиля);
	
	Если Профиль.Роли.Количество()            <> ОписаниеРолейПрофиля.Количество()
	 Или Профиль.ВидыДоступа.Количество()     <> СвойстваПрофиля.ВидыДоступа.Количество()
	 Или Профиль.ЗначенияДоступа.Количество() <> СвойстваПрофиля.ЗначенияДоступа.Количество()
	 Или Профиль.Назначение.Количество()      <> СвойстваПрофиля.Назначение.Количество() Тогда
		Возврат Истина;
	КонецЕсли;
	
	Для Каждого Роль Из ОписаниеРолейПрофиля Цикл
		МетаданныеРоли = Метаданные.Роли.Найти(Роль);
		Если МетаданныеРоли = Неопределено Тогда
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'В поставляемом профиле ""%1""
				           |указана несуществующая роль ""%2"".'"),
				СвойстваПрофиля.Наименование,
				Роль);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		ИдентификаторРоли = ОбщегоНазначения.ИдентификаторОбъектаМетаданных(МетаданныеРоли);
		Если Профиль.Роли.НайтиСтроки(Новый Структура("Роль", ИдентификаторРоли)).Количество() = 0 Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Для Каждого ОписаниеВидаДоступа Из СвойстваПрофиля.ВидыДоступа Цикл
		СвойстваВидаДоступа = УправлениеДоступомСлужебный.СвойстваВидаДоступа(ОписаниеВидаДоступа.Ключ);
		Отбор = Новый Структура;
		Отбор.Вставить("ВидДоступа",        СвойстваВидаДоступа.Ссылка);
		Отбор.Вставить("Предустановленный", ОписаниеВидаДоступа.Значение = "Предустановленный");
		Отбор.Вставить("ВсеРазрешены",      ОписаниеВидаДоступа.Значение = "ВначалеВсеРазрешены");
		Если Профиль.ВидыДоступа.НайтиСтроки(Отбор).Количество() = 0 Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Для Каждого ОписаниеЗначенияДоступа Из СвойстваПрофиля.ЗначенияДоступа Цикл
		Отбор = Новый Структура;
		Отбор.Вставить("ЗначениеДоступа", ПредопределенноеЗначение(ОписаниеЗначенияДоступа.ЗначениеДоступа));
		Если Профиль.ЗначенияДоступа.НайтиСтроки(Отбор).Количество() = 0 Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Для Каждого ТипПользователей Из СвойстваПрофиля.Назначение Цикл
		Отбор = Новый Структура;
		Отбор.Вставить("ТипПользователей", ТипПользователей);
		Если Профиль.Назначение.НайтиСтроки(Отбор).Количество() = 0 Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Ложь;
	
КонецФункции

// Сравнивает часть объекта в базе данных с той же частью объекта в памяти.
//
// Параметры:
//  Профиль - СправочникОбъект.ПрофилиГруппДоступа - объект в памяти.
//
//  СтарыеЗначения - Структура:
//    * Наименование    - Строка
//    * Родитель        - СправочникСсылка.ПрофилиГруппДоступа
//    * Роли            - РезультатЗапроса
//    * Назначение      - РезультатЗапроса
//    * ВидыДоступа     - РезультатЗапроса
//    * ЗначенияДоступа - РезультатЗапроса
//
Функция ПоставляемыеОбластиПрофиляИзменены(НовыйПрофиль, СтарыеЗначения) Экспорт
	
	Если ВРег(НовыйПрофиль.Наименование) <> ВРег(СтарыеЗначения.Наименование) Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если НовыйПрофиль.Родитель <> СтарыеЗначения.Родитель Тогда
		ОписанияПрофилей = УправлениеДоступомСлужебный.ПоставляемыеПрофили().ОписанияПрофилей;
		СвойстваПрофиля = ОписанияПрофилей.Получить(Строка(НовыйПрофиль.ИдентификаторПоставляемыхДанных)); // См. СвойстваПоставляемогоПрофиля
		Если СвойстваПрофиля <> Неопределено
		   И ЗначениеЗаполнено(СвойстваПрофиля.Родитель)
		   И ЗначениеЗаполнено(ПоставляемыйПрофильПоИдентификатору(СвойстваПрофиля.Родитель)) Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЕсли;
	
	Если НовыйПрофиль.ЭтоГруппа Тогда
		Возврат Ложь;
	КонецЕсли;
	
	СтарыйПрофиль = Новый Структура;
	СтарыйПрофиль.Вставить("Роли",            СтарыеЗначения.Роли.Выгрузить());
	СтарыйПрофиль.Вставить("Назначение",      СтарыеЗначения.Назначение.Выгрузить());
	СтарыйПрофиль.Вставить("ВидыДоступа",     СтарыеЗначения.ВидыДоступа.Выгрузить());
	СтарыйПрофиль.Вставить("ЗначенияДоступа", СтарыеЗначения.ЗначенияДоступа.Выгрузить());
	
	Если НовыйПрофиль.Роли.Количество()            <> СтарыйПрофиль.Роли.Количество()
	 Или НовыйПрофиль.ВидыДоступа.Количество()     <> СтарыйПрофиль.ВидыДоступа.Количество()
	 Или НовыйПрофиль.ЗначенияДоступа.Количество() <> СтарыйПрофиль.ЗначенияДоступа.Количество()
	 Или НовыйПрофиль.Назначение.Количество()      <> СтарыйПрофиль.Назначение.Количество() Тогда
		Возврат Истина;
	КонецЕсли;
	
	Для Каждого Строка Из НовыйПрофиль.Роли Цикл
		Если СтарыйПрофиль.Роли.Найти(Строка.Роль, "Роль") = Неопределено Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Для Каждого Строка Из НовыйПрофиль.ВидыДоступа Цикл
		Отбор = Новый Структура("ВидДоступа, Предустановленный, ВсеРазрешены",
			Строка.ВидДоступа, Строка.Предустановленный, Строка.ВсеРазрешены);
		Если СтарыйПрофиль.ВидыДоступа.НайтиСтроки(Отбор).Количество() = 0 Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Для Каждого Строка Из НовыйПрофиль.ВидыДоступа Цикл
		Отбор = Новый Структура("ВидДоступа, Предустановленный, ВсеРазрешены",
			Строка.ВидДоступа, Строка.Предустановленный, Строка.ВсеРазрешены);
		Если СтарыйПрофиль.ВидыДоступа.НайтиСтроки(Отбор).Количество() = 0 Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Для Каждого Строка Из НовыйПрофиль.ЗначенияДоступа Цикл
		Отбор = Новый Структура("ВидДоступа, ЗначениеДоступа, ВключаяНижестоящие",
			Строка.ВидДоступа, Строка.ЗначениеДоступа, Строка.ВключаяНижестоящие);
		Если СтарыйПрофиль.ЗначенияДоступа.НайтиСтроки(Отбор).Количество() = 0 Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Для Каждого Строка Из НовыйПрофиль.Назначение Цикл
		Если СтарыйПрофиль.Назначение.Найти(Строка.ТипПользователей, "ТипПользователей") = Неопределено Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Ложь;
	
КонецФункции

// Определяет наличие начального заполнения для профиля групп доступа в переопределяемом модуле.
//
// Параметры:
//  Профиль      - СправочникСсылка.ПрофилиГруппДоступа.
//
// Возвращаемое значение:
//  Булево
//
Функция ЕстьНачальноеЗаполнениеПрофиля(Знач Профиль) Экспорт
	
	ИдентификаторПоставляемыхДанных = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(
		Профиль, "ИдентификаторПоставляемыхДанных");
	
	Если Не ЗначениеЗаполнено(ИдентификаторПоставляемыхДанных) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ПоставляемыеПрофили = УправлениеДоступомСлужебный.ПоставляемыеПрофили();
	СвойстваПрофиля = ПоставляемыеПрофили.ОписанияПрофилей.Получить(Строка(ИдентификаторПоставляемыхДанных));
	
	Возврат СвойстваПрофиля <> Неопределено;
	
КонецФункции

// Определяет запрет изменения поставляемого профиля.
// Не поставляемый профиль не может иметь запрета изменения.
//
// Параметры:
//  Профиль      - СправочникОбъект.ПрофилиГруппДоступа
//               - ДанныеФормыСтруктура - созданные по объекту.
//
// РодительТолькоПросмотр - Булево - возвращаемое значение - устанавливается Истина,
//                 если родитель заполнен у поставляемого профиля и
//                 изменение поставляемого профиля запрещено.
//
// Возвращаемое значение:
//  Булево
//
Функция ЗапретИзмененияПрофиля(Знач Профиль, РодительТолькоПросмотр = Ложь) Экспорт
	
	Если Не ЗначениеЗаполнено(Профиль.ИдентификаторПоставляемыхДанных) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если Профиль.ИдентификаторПоставляемыхДанных =
			Новый УникальныйИдентификатор(ИдентификаторПрофиляАдминистратор()) Тогда
		// Изменение профиля Администратор всегда запрещено.
		Возврат Истина;
	КонецЕсли;
	
	ПоставляемыеПрофили = УправлениеДоступомСлужебный.ПоставляемыеПрофили();
	
	СвойстваПрофиля = ПоставляемыеПрофили.ОписанияПрофилей.Получить(
		Строка(Профиль.ИдентификаторПоставляемыхДанных));
	
	ЗапретИзменения = СвойстваПрофиля <> Неопределено
	      И (ПоставляемыеПрофили.ПараметрыОбновления.ЗапретитьИзменениеПрофилей
	         Или СвойстваПрофиля.ЭтоГруппа);
	
	Если ЗапретИзменения И ЗначениеЗаполнено(СвойстваПрофиля.Родитель) Тогда
		РодительТолькоПросмотр = Истина;
	КонецЕсли;
	
	Возврат ЗапретИзменения;
	
КонецФункции

// Возвращает описание назначения поставляемого профиля.
//
// Параметры:
//  Профиль - СправочникСсылка.ПрофилиГруппДоступа.
//
// Возвращаемое значение:
//  Строка.
//
Функция ПояснениеПоставляемогоПрофиля(Профиль) Экспорт
	
	ИдентификаторПоставляемыхДанных = Строка(ОбщегоНазначения.ЗначениеРеквизитаОбъекта(
		Профиль, "ИдентификаторПоставляемыхДанных"));
	
	ПояснениеПоставляемыхПрофилей = УправлениеДоступомСлужебныйПовтИсп.ПояснениеПоставляемыхПрофилей();
	
	Возврат Строка(ПояснениеПоставляемыхПрофилей.Получить(ИдентификаторПоставляемыхДанных));
	
КонецФункции

// Создает поставляемый профиль в справочнике ПрофилиГруппДоступа, свойственный
// прикладному решению и позволяет перезаполнить ранее созданный поставляемый профиль
// по его поставляемому описанию.
//  Поиск начального заполнения осуществляется по строке уникального идентификатора профиля.
//
// Параметры:
//  Профиль      - СправочникСсылка.ПрофилиГруппДоступа.
//                 Если для указанного профиля описание начального заполнения найдено,
//                 содержимое профиля полностью замещается.
//
//  ОбновитьГруппыДоступа - Булево - если Истина, виды доступа групп доступа профиля будут обновлены.
//
Процедура ЗаполнитьПоставляемыйПрофиль(Знач Профиль, Знач ОбновитьГруппыДоступа) Экспорт
	
	ИдентификаторПоставляемыхДанных = Строка(ОбщегоНазначения.ЗначениеРеквизитаОбъекта(
		Профиль, "ИдентификаторПоставляемыхДанных"));
	
	ПоставляемыеПрофили = УправлениеДоступомСлужебный.ПоставляемыеПрофили();
	СвойстваПрофиля = ПоставляемыеПрофили.ОписанияПрофилей.Получить(ИдентификаторПоставляемыхДанных);
	
	Если СвойстваПрофиля <> Неопределено Тогда
		
		ОбновитьПрофильИлиПапкуПрофилей(СвойстваПрофиля);
		
		Если ОбновитьГруппыДоступа И Не СвойстваПрофиля.ЭтоГруппа Тогда
			Справочники.ГруппыДоступа.ОбновитьГруппыДоступаПрофиля(Профиль, Истина);
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Возвращает список ссылок на профили, содержащие недоступные роли или роли помеченные на удаление.
//
// Возвращаемое значение:
//  Массив - массив элементов СправочникСсылка.ПрофилиГруппДоступа.
//
Функция НесовместимыеПрофилиГруппДоступа() Экспорт
	
	Выгрузка = НазначениеИРолиПрофилейГруппаДоступа();
	
	НесовместимыеПрофили = Новый Массив;
	НедоступныеРолиПоНазначению = Новый Соответствие;
	
	Для Каждого ОписаниеПрофиля Из Выгрузка Цикл
		НазначениеПрофиля = УправлениеДоступомСлужебныйКлиентСервер.НазначениеПрофиля(ОписаниеПрофиля);
		НедоступныеРоли = НедоступныеРолиПоНазначению.Получить(НазначениеПрофиля);
		Если НедоступныеРоли = Неопределено Тогда
			НедоступныеРоли = ПользователиСлужебныйПовтИсп.НедоступныеРоли(НазначениеПрофиля);
			НедоступныеРолиПоНазначению.Вставить(НазначениеПрофиля, НедоступныеРоли);
		КонецЕсли;
		
		Если ОписаниеПрофиля.Роли.Найти(Неопределено, "Роль") <> Неопределено Тогда
			НесовместимыеПрофили.Добавить(ОписаниеПрофиля.Ссылка);
			Продолжить;
		КонецЕсли;
		
		ОписаниеРолей = ОбщегоНазначения.ОбъектыМетаданныхПоИдентификаторам(
			ОписаниеПрофиля.Роли.ВыгрузитьКолонку("Роль"), Ложь);
		
		Для Каждого ОписаниеРоли Из ОписаниеРолей Цикл
			ОбъектМетаданных = ОписаниеРоли.Значение; // ОбъектМетаданных
			Если ОбъектМетаданных = Неопределено Тогда
				// Роль, недоступная до перезапуска, не является проблемой.
				Продолжить;
			КонецЕсли;
			
			Если ОбъектМетаданных = Null
			 Или НедоступныеРоли.Получить(ОбъектМетаданных.Имя) <> Неопределено
			 Или ВРег(Лев(ОбъектМетаданных.Имя, СтрДлина("Удалить"))) = ВРег("Удалить") Тогда
				
				НесовместимыеПрофили.Добавить(ОписаниеПрофиля.Ссылка);
				Прервать;
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
	Возврат НесовместимыеПрофили;
	
КонецФункции

// Только для вызова из УправлениеДоступомСлужебный.ПоставляемыеПрофили.
// Смотри также ПроверенныеПоставляемыеПрофилиСеанса.
// 
// Возвращаемое значение:
//   см. УправлениеДоступомСлужебный.ПоставляемыеПрофили
//
Функция ПоставляемыеПрофили() Экспорт
	
	Кэш = УправлениеДоступомСлужебныйПовтИсп.ОписаниеПоставляемыхПрофилейСеанса();
	
	ТекущаяДатаСеанса = ТекущаяДатаСеанса();
	Если Кэш.Проверка.Дата + 3 > ТекущаяДатаСеанса Тогда
		Возврат Кэш.ПоставляемыеПрофилиСеанса;
	КонецЕсли;
	
	НовоеЗначение = Кэш.ХешСумма;
	
	ИмяПараметра = "СтандартныеПодсистемы.УправлениеДоступом.ОписаниеПоставляемыхПрофилей";
	СтароеЗначение = СтандартныеПодсистемыСервер.ПараметрРаботыРасширения(ИмяПараметра, Истина);
	
	Если СтароеЗначение <> НовоеЗначение Тогда
		Блокировка = Новый БлокировкаДанных;
		ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ПараметрыРаботыВерсийРасширений");
		ЭлементБлокировки.УстановитьЗначение("ВерсияРасширений", Справочники.ВерсииРасширений.ПустаяСсылка());
		ЭлементБлокировки.УстановитьЗначение("ИмяПараметра", ИмяПараметра);
		НачатьТранзакцию();
		Попытка
			Блокировка.Заблокировать();
			УжеИзменен = Ложь;
			СтароеЗначение = СтандартныеПодсистемыСервер.ПараметрРаботыРасширения(ИмяПараметра, Истина, УжеИзменен);
			Если СтароеЗначение <> НовоеЗначение Тогда
				Если УжеИзменен Тогда
					УправлениеДоступомСлужебный.ПроверитьАктуальностьМетаданных();
				КонецЕсли;
				УстановитьОтключениеБезопасногоРежима(Истина);
				УстановитьПривилегированныйРежим(Истина);
				СтандартныеПодсистемыСервер.УстановитьПараметрРаботыРасширения(ИмяПараметра, НовоеЗначение, Истина);
				УстановитьПривилегированныйРежим(Ложь);
				УстановитьОтключениеБезопасногоРежима(Ложь);
			КонецЕсли;
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
	КонецЕсли;
	
	Кэш.Проверка.Дата = ТекущаяДатаСеанса;
	
	Возврат Кэш.ПоставляемыеПрофилиСеанса;
	
КонецФункции

Функция ИзменилисьВидыИлиЗначенияДоступаИлиНазначение(СтарыеЗначения, ТекущийОбъект) Экспорт
	
	Если СтарыеЗначения.Ссылка <> ТекущийОбъект.Ссылка Тогда
		Возврат Истина;
	КонецЕсли;
	
	ВидыДоступа     = СтарыеЗначения.ВидыДоступа.Выгрузить();
	ЗначенияДоступа = СтарыеЗначения.ЗначенияДоступа.Выгрузить();
	Назначение      = СтарыеЗначения.Назначение.Выгрузить();
	
	Если ВидыДоступа.Количество()     <> ТекущийОбъект.ВидыДоступа.Количество()
	 Или ЗначенияДоступа.Количество() <> ТекущийОбъект.ЗначенияДоступа.Количество()
	 Или Назначение.Количество()      <> ТекущийОбъект.Назначение.Количество() Тогда
		
		Возврат Истина;
	КонецЕсли;
	
	Отбор = Новый Структура("ВидДоступа, Предустановленный, ВсеРазрешены");
	Для Каждого Строка Из ТекущийОбъект.ВидыДоступа Цикл
		ЗаполнитьЗначенияСвойств(Отбор, Строка);
		Если ВидыДоступа.НайтиСтроки(Отбор).Количество() = 0 Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Отбор = Новый Структура("ВидДоступа, ЗначениеДоступа");
	Для Каждого Строка Из ТекущийОбъект.ЗначенияДоступа Цикл
		ЗаполнитьЗначенияСвойств(Отбор, Строка);
		Если ЗначенияДоступа.НайтиСтроки(Отбор).Количество() = 0 Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Отбор = Новый Структура("ТипПользователей");
	Для Каждого Строка Из ТекущийОбъект.Назначение Цикл
		ЗаполнитьЗначенияСвойств(Отбор, Строка);
		Если Назначение.НайтиСтроки(Отбор).Количество() = 0 Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Ложь;
	
КонецФункции

// Параметры:
//  ИзменениеЯзыков - см. МультиязычностьСервер.ОписаниеСтарыхИНовыхНастроекЯзыков
//
Процедура ПриИзмененииЯзыкаИнформационнойБазы(ИзменениеЯзыков) Экспорт
	
	УправлениеДоступомСлужебный.ПоставляемыеПрофили(); // Проверка актуальности метаданных.
	ОписанияПрофилей = ЗаполненныеПоставляемыеПрофили().ОписанияПрофилей;
	
	НовыеНаименованияПрофилей = Новый ТаблицаЗначений;
	НовыеНаименованияПрофилей.Колонки.Добавить("ИдентификаторПоставляемыхДанных",
		Новый ОписаниеТипов("УникальныйИдентификатор"));
	НовыеНаименованияПрофилей.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка",,,,
		Новый КвалификаторыСтроки(Метаданные.Справочники.ПрофилиГруппДоступа.ДлинаНаименования)));
	
	Для Каждого ОписаниеПрофиля Из ОписанияПрофилей Цикл
		Наименование = ?(ОписаниеПрофиля.Свойство("Наименование"), ОписаниеПрофиля.Наименование, "");
		Если Не ЗначениеЗаполнено(Наименование)
		 Или Не СтроковыеФункцииКлиентСервер.ЭтоУникальныйИдентификатор(ОписаниеПрофиля.Идентификатор) Тогда
			Продолжить;
		КонецЕсли;
		НоваяСтрока = НовыеНаименованияПрофилей.Добавить();
		НоваяСтрока.ИдентификаторПоставляемыхДанных =
			Новый УникальныйИдентификатор(ОписаниеПрофиля.Идентификатор);
		НоваяСтрока.Наименование = Наименование;
	КонецЦикла;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("НовыеНаименованияПрофилей", НовыеНаименованияПрофилей);
	Запрос.Текст =
	"ВЫБРАТЬ
	|	НовыеНаименованияПрофилей.ИдентификаторПоставляемыхДанных КАК ИдентификаторПоставляемыхДанных,
	|	НовыеНаименованияПрофилей.Наименование КАК Наименование
	|ПОМЕСТИТЬ НовыеНаименованияПрофилей
	|ИЗ
	|	&НовыеНаименованияПрофилей КАК НовыеНаименованияПрофилей
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	Профили.Ссылка КАК Ссылка,
	|	НовыеНаименованияПрофилей.Наименование КАК Наименование
	|ИЗ
	|	Справочник.ПрофилиГруппДоступа КАК Профили
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ НовыеНаименованияПрофилей КАК НовыеНаименованияПрофилей
	|		ПО Профили.ИдентификаторПоставляемыхДанных = НовыеНаименованияПрофилей.ИдентификаторПоставляемыхДанных
	|			И Профили.Наименование <> НовыеНаименованияПрофилей.Наименование";
	
	Выборка = Запрос.Выполнить().Выбрать();
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("Справочник.ПрофилиГруппДоступа");
	
	Пока Выборка.Следующий() Цикл
		ЭлементБлокировки.УстановитьЗначение("Ссылка", Выборка.Ссылка);
		НачатьТранзакцию();
		Попытка
			Блокировка.Заблокировать();
			ТекущийОбъект = Выборка.Ссылка.ПолучитьОбъект(); // СправочникОбъект.ПрофилиГруппДоступа
			Если ТекущийОбъект.Наименование <> Выборка.Наименование Тогда
				ТекущийОбъект.Наименование = Выборка.Наименование;
				ОбновлениеИнформационнойБазы.ЗаписатьОбъект(ТекущийОбъект);
			КонецЕсли;
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
	КонецЦикла;
	
КонецПроцедуры

Процедура ВосстановитьНесуществующиеВидыИЗначенияДоступа(СтарыеЗначения, НовыеЗначения) Экспорт
	
	Если ТипЗнч(СтарыеЗначения.ВидыДоступа) <> Тип("РезультатЗапроса") Тогда
		Возврат;
	КонецЕсли;
	
	СвойстваВидовДоступа = УправлениеДоступомСлужебный.СвойстваВидовДоступа();
	
	ПоСсылкам             = СвойстваВидовДоступа.ПоСсылкам;
	ПоТипамГруппИЗначений = СвойстваВидовДоступа.ПоТипамГруппИЗначений;
	
	СтарыеВидыДоступа     = СтарыеЗначения.ВидыДоступа.Выгрузить();
	СтарыеЗначенияДоступа = СтарыеЗначения.ЗначенияДоступа.Выгрузить();
	
	ЕстьВосстановленные = Ложь;
	
	Для Каждого Строка Из СтарыеВидыДоступа Цикл
		Если Строка.ВидДоступа = Неопределено
		 Или ПоСсылкам.Получить(Строка.ВидДоступа) <> Неопределено
		 Или НовыеЗначения.ВидыДоступа.Найти(Строка.ВидДоступа, "ВидДоступа") <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		НоваяСтрока = НовыеЗначения.ВидыДоступа.Добавить();
		ЗаполнитьЗначенияСвойств(НоваяСтрока, Строка);
		ЕстьВосстановленные = Истина;
	КонецЦикла;
	
	Для Каждого Строка Из СтарыеЗначенияДоступа Цикл
		Если Строка.ВидДоступа = Неопределено
		 Или Строка.ЗначениеДоступа = Неопределено
		 Или НовыеЗначения.ВидыДоступа.Найти(Строка.ВидДоступа, "ВидДоступа") = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		СвойстваВидаДоступа = ПоТипамГруппИЗначений.Получить(ТипЗнч(Строка.ЗначениеДоступа)); // См. УправлениеДоступомСлужебный.СвойстваВидовДоступа
		Если СвойстваВидаДоступа <> Неопределено
		   И СвойстваВидаДоступа.Ссылка = Строка.ВидДоступа Тогда
			Продолжить;
		КонецЕсли;
		Отбор = Новый Структура("ВидДоступа, ЗначениеДоступа",
			Строка.ВидДоступа, Строка.ЗначениеДоступа);
		Если НовыеЗначения.ЗначенияДоступа.НайтиСтроки(Отбор).Количество() > 0 Тогда
			Продолжить;
		КонецЕсли;
		НоваяСтрока = НовыеЗначения.ЗначенияДоступа.Добавить();
		ЗаполнитьЗначенияСвойств(НоваяСтрока, Строка);
		ЕстьВосстановленные = Истина;
	КонецЦикла;
	
	Если ЕстьВосстановленные Тогда
		// Для вычисления изменений при обновлении поставляемых профилей
		// с подключенными расширениями, чтобы обновились вспомогательные данные.
		НовыеЗначения.ВидыДоступа.Добавить();
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ОбновитьСтандартныеРолиРасширений.
Функция СтандартныеРолиПрофиля(РолиПрофиля)
	
	Результат = Новый Структура;
	Результат.Вставить("АдминистраторСистемы",
		ЕстьРольВПрофиле(РолиПрофиля, Метаданные.Роли.АдминистраторСистемы));
	
	Результат.Вставить("ПолныеПрава",
		ЕстьРольВПрофиле(РолиПрофиля, Метаданные.Роли.ПолныеПрава));
	
	Результат.Вставить("БазовыеПраваБСП",
		ЕстьРольВПрофиле(РолиПрофиля, Метаданные.Роли.БазовыеПраваБСП));
	
	Результат.Вставить("БазовыеПраваВнешнихПользователейБСП",
		ЕстьРольВПрофиле(РолиПрофиля, Метаданные.Роли.БазовыеПраваВнешнихПользователейБСП));
	
	Возврат Результат;
	
КонецФункции

// Для процедуры ОбновитьСтандартныеРолиРасширений.
Функция ЕстьРольВПрофиле(РолиПрофиля, Роль)
	
	Результат = Ложь;
	
	Если ТипЗнч(РолиПрофиля) = Тип("Массив") Тогда
		Если РолиПрофиля.Найти(Роль.Имя) <> Неопределено Тогда
			Результат = Истина;
		КонецЕсли;
	ИначеЕсли ТипЗнч(РолиПрофиля) = Тип("ДанныеФормыКоллекция") Тогда
		Отбор = Новый Структура("Роль", Роль.Имя);
		Если РолиПрофиля.НайтиСтроки(Отбор).Количество() > 0 Тогда
			Результат = Истина;
		КонецЕсли;
	Иначе
		ИдентификаторРоли = ОбщегоНазначения.ИдентификаторОбъектаМетаданных(Роль.ПолноеИмя());
		
		Если РолиПрофиля.Найти(ИдентификаторРоли, "Роль") <> Неопределено Тогда
			Результат = Истина;
		КонецЕсли;
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Для процедуры ОбновитьСтандартныеРолиРасширений.
Процедура УстановитьРолиВПрофиле(РолиПрофиля, ИменаРолей, Установить)
	
	Если Не ЗначениеЗаполнено(ИменаРолей) Тогда
		Возврат;
	КонецЕсли;
	
	Если ТипЗнч(РолиПрофиля) = Тип("Массив") Тогда
		Для Каждого ИмяРоли Из ИменаРолей Цикл
			ИндексРоли = РолиПрофиля.Найти(ИмяРоли);
			Установлена = ИндексРоли <> Неопределено;
			Если Установить И Не Установлена Тогда
				РолиПрофиля.Добавить(ИмяРоли);
			ИначеЕсли Не Установить И Установлена Тогда
				РолиПрофиля.Удалить(ИндексРоли);
			КонецЕсли;
		КонецЦикла;
	ИначеЕсли ТипЗнч(РолиПрофиля) = Тип("ДанныеФормыКоллекция") Тогда
		Для Каждого ИмяРоли Из ИменаРолей Цикл
			Отбор = Новый Структура("Роль", ИмяРоли);
			НайденныеСтроки = РолиПрофиля.НайтиСтроки(Отбор);
			Установлена = НайденныеСтроки.Количество() > 0;
			Если Установить И Не Установлена Тогда
				РолиПрофиля.Добавить().Роль = ИмяРоли;
			ИначеЕсли Не Установить И Установлена Тогда
				НайденнаяСтрока = НайденныеСтроки[0]; // ДанныеФормыЭлементКоллекции
				РолиПрофиля.Удалить(НайденнаяСтрока);
			КонецЕсли;
		КонецЦикла;
	Иначе
		СтрокаПолныхИменРолей = "Роль." + СтрСоединить(ИменаРолей, Символы.ПС + "Роль.");
		ПолныеИменаРолей = СтрРазделить(СтрокаПолныхИменРолей, Символы.ПС);
		ИдентификаторыРолей = ОбщегоНазначения.ИдентификаторыОбъектовМетаданных(ПолныеИменаРолей);
		
		Для Каждого ПолноеИмяРоли Из ПолныеИменаРолей Цикл
			ИдентификаторРоли = ИдентификаторыРолей.Получить(ПолноеИмяРоли);
			СтрокаТЧ = РолиПрофиля.Найти(ИдентификаторРоли, "Роль");
			Установлена = СтрокаТЧ <> Неопределено;
			Если Установить И Не Установлена Тогда
				РолиПрофиля.Добавить().Роль = ИдентификаторРоли;
			ИначеЕсли Не Установить И Установлена Тогда
				РолиПрофиля.Удалить(СтрокаТЧ);
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры

Процедура ОчиститьУдаленныеСтандартныеРолиРасширений(РолиПрофиля)
	
	Если ТипЗнч(РолиПрофиля) = Тип("Массив")
	 Или ТипЗнч(РолиПрофиля) = Тип("ДанныеФормыКоллекция") Тогда
		Возврат;
	КонецЕсли;
	
	ИдентификаторыРолей = Новый Массив;
	Для Каждого Строка Из РолиПрофиля Цикл
		Если ТипЗнч(Строка.Роль) = Тип("СправочникСсылка.ИдентификаторыОбъектовРасширений") Тогда
			ИдентификаторыРолей.Добавить(Строка.Роль);
		КонецЕсли;
	КонецЦикла;
	
	РолиПоИдентификаторам = ОбщегоНазначения.ОбъектыМетаданныхПоИдентификаторам(ИдентификаторыРолей, Ложь);
	ИдентификаторыУдаленныхИОтключенныхРолей = Новый Массив;
	Для Каждого ОписаниеРоли Из РолиПоИдентификаторам Цикл
		Если ТипЗнч(ОписаниеРоли.Значение) = Тип("ОбъектМетаданных") Тогда
			Продолжить;
		КонецЕсли;
		ИдентификаторыУдаленныхИОтключенныхРолей.Добавить(ОписаниеРоли.Ключ);
	КонецЦикла;
	
	ПолныеИменаУдаленныхИОтключенныхРолей =
		Справочники.ИдентификаторыОбъектовМетаданных.ПолныеИменаОбъектовМетаданныхВключаяУдаленные(
			ИдентификаторыУдаленныхИОтключенныхРолей);
	
	Для Каждого ОписаниеРоли Из ПолныеИменаУдаленныхИОтключенныхРолей Цикл
		ЧастиИмени = СтрРазделить(ОписаниеРоли.Значение, ".");
		Если ЧастиИмени.Количество() <> 2
		 Или ВРег(ЧастиИмени[0]) <> ВРег("Роль") Тогда
			Продолжить;
		КонецЕсли;
		ИмяРоли = ЧастиИмени[1];
		Если Не СтрЗаканчиваетсяНа(ВРег(ИмяРоли), ВРег("ОбщиеПрава"))
		   И Не СтрЗаканчиваетсяНа(ВРег(ИмяРоли), ВРег("ПолныеПрава"))
		   И Не СтрЗаканчиваетсяНа(ВРег(ИмяРоли), ВРег("БазовыеПрава"))
		   И Не СтрЗаканчиваетсяНа(ВРег(ИмяРоли), ВРег("БазовыеПраваВнешнихПользователей"))
		   И Не СтрЗаканчиваетсяНа(ВРег(ИмяРоли), ВРег("АдминистраторСистемы")) Тогда
			Продолжить;
		КонецЕсли;
		Отбор = Новый Структура("Роль", ОписаниеРоли.Ключ);
		Строки = РолиПрофиля.НайтиСтроки(Отбор);
		Для Каждого Строка Из Строки Цикл
			РолиПрофиля.Удалить(Строка);
		КонецЦикла;
	КонецЦикла;
	
КонецПроцедуры

// Только для вызова из УправлениеДоступомСлужебный.СтандартныеРолиРасширений.
// 
// Возвращаемое значение:
//   см. УправлениеДоступомСлужебный.СтандартныеРолиРасширений
//
Функция СтандартныеРолиРасширений() Экспорт
	
	Кэш = УправлениеДоступомСлужебныйПовтИсп.ОписаниеСтандартныхРолейРасширенийСеанса();
	
	ТекущаяДатаСеанса = ТекущаяДатаСеанса();
	Если Кэш.Проверка.Дата + 3 > ТекущаяДатаСеанса Тогда
		Возврат Кэш.РолиСеанса;
	КонецЕсли;
	
	НовоеЗначение = Кэш.ХешСумма;
	
	ИмяПараметра = "СтандартныеПодсистемы.УправлениеДоступом.ОписаниеСтандартныхРолейРасширений";
	СтароеЗначение = СтандартныеПодсистемыСервер.ПараметрРаботыРасширения(ИмяПараметра, Истина);
	
	Если СтароеЗначение <> НовоеЗначение Тогда
		Блокировка = Новый БлокировкаДанных;
		ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ПараметрыРаботыВерсийРасширений");
		ЭлементБлокировки.УстановитьЗначение("ВерсияРасширений", Справочники.ВерсииРасширений.ПустаяСсылка());
		ЭлементБлокировки.УстановитьЗначение("ИмяПараметра", ИмяПараметра);
		НачатьТранзакцию();
		Попытка
			Блокировка.Заблокировать();
			УжеИзменен = Ложь;
			СтароеЗначение = СтандартныеПодсистемыСервер.ПараметрРаботыРасширения(ИмяПараметра, Истина, УжеИзменен);
			Если СтароеЗначение <> НовоеЗначение Тогда
				Если УжеИзменен Тогда
					УправлениеДоступомСлужебный.ПроверитьАктуальностьМетаданных();
				КонецЕсли;
				УстановитьОтключениеБезопасногоРежима(Истина);
				УстановитьПривилегированныйРежим(Истина);
				СтандартныеПодсистемыСервер.УстановитьПараметрРаботыРасширения(ИмяПараметра, НовоеЗначение, Истина);
				УстановитьПривилегированныйРежим(Ложь);
				УстановитьОтключениеБезопасногоРежима(Ложь);
			КонецЕсли;
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
	КонецЕсли;
	
	Кэш.Проверка.Дата = ТекущаяДатаСеанса;
	
	Возврат Кэш.РолиСеанса;
	
КонецФункции

// Для вызова из УправлениеДоступомСлужебныйПовтИсп.ОписаниеСтандартныхРолейРасширенийСеанса
// 
// Параметры:
//  ХешСумма - Строка - возвращаемое значение.
//
// Возвращаемое значение:
//   см. УправлениеДоступомСлужебный.СтандартныеРолиРасширений
//
Функция ПодготовленныеСтандартныеРолиРасширенийСеанса(ХешСумма) Экспорт
	
	ОбщиеПрава                       = Новый СписокЗначений;
	БазовыеПрава                     = Новый СписокЗначений;
	БазовыеПраваВнешнихПользователей = Новый СписокЗначений;
	АдминистраторСистемы             = Новый СписокЗначений;
	ПолныеПрава                      = Новый СписокЗначений;
	ДополнительныеРолиАдминистратора = Новый СписокЗначений;
	ВсеРоли                          = Новый Соответствие;
	
	РазделениеВключено = ОбщегоНазначения.РазделениеВключено();
	
	Для Каждого Роль Из Метаданные.Роли Цикл
		Расширение = Роль.РасширениеКонфигурации();
		Если Расширение = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		ИмяРоли = Роль.Имя;
		
		Если СтрЗаканчиваетсяНа(ВРег(ИмяРоли), ВРег("ОбщиеПрава")) Тогда
			ОбщиеПрава.Добавить(ИмяРоли);
			ДополнительныеРолиАдминистратора.Добавить(ИмяРоли);
			ВсеРоли.Вставить(ИмяРоли, "ОбщиеПрава");
			
		ИначеЕсли СтрЗаканчиваетсяНа(ВРег(ИмяРоли), ВРег("ПолныеПрава")) Тогда
			ПолныеПрава.Добавить(ИмяРоли);
			ДополнительныеРолиАдминистратора.Добавить(ИмяРоли);
			ВсеРоли.Вставить(ИмяРоли, "ПолныеПрава");
			
		ИначеЕсли СтрЗаканчиваетсяНа(ВРег(ИмяРоли), ВРег("БазовыеПрава")) Тогда
			БазовыеПрава.Добавить(ИмяРоли);
			ВсеРоли.Вставить(ИмяРоли, "БазовыеПрава");
			
		ИначеЕсли СтрЗаканчиваетсяНа(ВРег(ИмяРоли), ВРег("БазовыеПраваВнешнихПользователей")) Тогда
			БазовыеПраваВнешнихПользователей.Добавить(ИмяРоли);
			ВсеРоли.Вставить(ИмяРоли, "БазовыеПраваВнешнихПользователей");
			
		ИначеЕсли СтрЗаканчиваетсяНа(ВРег(ИмяРоли), ВРег("АдминистраторСистемы")) Тогда
			АдминистраторСистемы.Добавить(ИмяРоли);
			ВсеРоли.Вставить(ИмяРоли, "АдминистраторСистемы");
			Если Не РазделениеВключено Тогда
				ДополнительныеРолиАдминистратора.Добавить(ИмяРоли);
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
	ОбщиеПрава.СортироватьПоЗначению();
	БазовыеПрава.СортироватьПоЗначению();
	БазовыеПраваВнешнихПользователей.СортироватьПоЗначению();
	АдминистраторСистемы.СортироватьПоЗначению();
	ПолныеПрава.СортироватьПоЗначению();
	ДополнительныеРолиАдминистратора.СортироватьПоЗначению();
	
	ОбщиеПрава = ОбщиеПрава.ВыгрузитьЗначения();
	БазовыеПрава = БазовыеПрава.ВыгрузитьЗначения();
	БазовыеПраваВнешнихПользователей = БазовыеПраваВнешнихПользователей.ВыгрузитьЗначения();
	АдминистраторСистемы = АдминистраторСистемы.ВыгрузитьЗначения();
	ПолныеПрава = ПолныеПрава.ВыгрузитьЗначения();
	ДополнительныеРолиАдминистратора = ДополнительныеРолиАдминистратора.ВыгрузитьЗначения();
	
	ДополнительныеРоли = Новый Соответствие;
	Для Каждого ИмяРоли Из ДополнительныеРолиАдминистратора Цикл
		ДополнительныеРоли.Вставить(ИмяРоли, Истина);
	КонецЦикла;
	
	Свойства = Новый Структура;
	Свойства.Вставить("ОбщиеПрава",
		Новый ФиксированныйМассив(ОбщиеПрава));
	
	Свойства.Вставить("БазовыеПрава",
		Новый ФиксированныйМассив(БазовыеПрава));
	
	Свойства.Вставить("БазовыеПраваВнешнихПользователей",
		Новый ФиксированныйМассив(БазовыеПраваВнешнихПользователей));
	
	Свойства.Вставить("АдминистраторСистемы",
		Новый ФиксированныйМассив(АдминистраторСистемы));
	
	Свойства.Вставить("ПолныеПрава",
		Новый ФиксированныйМассив(ПолныеПрава));
	
	Свойства.Вставить("Все",
		Новый ФиксированноеСоответствие(ВсеРоли));
	
	Свойства.Вставить("ДополнительныеРолиАдминистратора",
		Новый ФиксированноеСоответствие(ДополнительныеРоли));
	
	ОписаниеВерсии = Новый Структура("СвойстваВерсии", Новый Массив);
	ДобавитьЭлементВерсии(ОписаниеВерсии, "Версия", "1");
	ДобавитьЭлементВерсии(ОписаниеВерсии, "ОбщиеПрава", ОбщиеПрава);
	ДобавитьЭлементВерсии(ОписаниеВерсии, "БазовыеПрава", БазовыеПрава);
	ДобавитьЭлементВерсии(ОписаниеВерсии, "БазовыеПраваВнешнихПользователей", БазовыеПраваВнешнихПользователей);
	ДобавитьЭлементВерсии(ОписаниеВерсии, "АдминистраторСистемы", АдминистраторСистемы);
	ДобавитьЭлементВерсии(ОписаниеВерсии, "ПолныеПрава", ПолныеПрава);
	ДобавитьЭлементВерсии(ОписаниеВерсии, "ДополнительныеРолиАдминистратора", ДополнительныеРолиАдминистратора);
	
	Результат = Новый ФиксированнаяСтруктура(Свойства);
	
	СтрокаВерсии = СтрСоединить(ОписаниеВерсии.СвойстваВерсии, Символы.ПС);
	ХешСумма = УправлениеДоступомСлужебный.ХешСуммаДанных(СтрокаВерсии);
	
	Возврат Результат;
	
КонецФункции

// См. УправлениеДоступомСлужебный.ДобавитьЭлементВерсии
Процедура ДобавитьЭлементВерсии(Контекст, ИмяПоля, Значение)
	УправлениеДоступомСлужебный.ДобавитьЭлементВерсии(Контекст, ИмяПоля, Значение);
КонецПроцедуры

// См. УправлениеДоступомСлужебный.ДобавитьСвойстваВерсии
Процедура ДобавитьСвойстваВерсии(Контекст, Структура, ИменаПолей)
	УправлениеДоступомСлужебный.ДобавитьСвойстваВерсии(Контекст, Структура, ИменаПолей);
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции для поддержки обмена данными в РИБ.

// Только для внутреннего использования.
//
// Параметры:
//  ЭлементДанных - СправочникОбъект.ПрофилиГруппДоступа
//
Процедура ВосстановитьСоставРолейРасширений(ЭлементДанных) Экспорт
	
	УдалитьРолиРасширений(ЭлементДанных);
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("Профиль", ЭлементДанных.Ссылка);
	Запрос.Текст =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	РолиПрофилей.Роль КАК Роль
	|ИЗ
	|	Справочник.ПрофилиГруппДоступа.Роли КАК РолиПрофилей
	|ГДЕ
	|	РолиПрофилей.Ссылка = &Профиль
	|	И ТИПЗНАЧЕНИЯ(РолиПрофилей.Роль) = ТИП(Справочник.ИдентификаторыОбъектовРасширений)";
	
	// Добавление ролей расширений к новому состав ролей конфигурации.
	Выборка = Запрос.Выполнить().Выбрать();
	Пока Выборка.Следующий() Цикл
		ЭлементДанных.Роли.Добавить().Роль = Выборка.Роль;
	КонецЦикла;
	
КонецПроцедуры

// Только для внутреннего использования.
Процедура УдалитьРолиРасширений(ЭлементДанных) Экспорт
	
	Индекс = ЭлементДанных.Роли.Количество() - 1;
	Пока Индекс >= 0 Цикл
		Если ТипЗнч(ЭлементДанных.Роли[Индекс].Роль) <> Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных") Тогда
			ЭлементДанных.Роли.Удалить(Индекс);
		КонецЕсли;
		Индекс = Индекс - 1;
	КонецЦикла;
	
КонецПроцедуры

// Только для внутреннего использования.
Процедура УдалитьРолиРасширенийВоВсехПрофиляхГруппДоступа() Экспорт
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	РолиПрофилей.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.ПрофилиГруппДоступа.Роли КАК РолиПрофилей
	|ГДЕ
	|	ТИПЗНАЧЕНИЯ(РолиПрофилей.Роль) <> ТИП(Справочник.ИдентификаторыОбъектовМетаданных)";
	
	ЕстьИзменения = Ложь;
	
	// АПК:1328-выкл. См. 648.1.1. Допустимо чтение без предварительной
	// управляемой разделяемой блокировки, так как используется только
	// для очистки и какой сеанс очистит первым не важно.
	Выборка = Запрос.Выполнить().Выбрать();
	// АПК:1328-вкл.
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("Справочник.ПрофилиГруппДоступа");
	
	Пока Выборка.Следующий() Цикл
		ЭлементБлокировки.УстановитьЗначение("Ссылка", Выборка.Ссылка);
		НачатьТранзакцию();
		Попытка
			Блокировка.Заблокировать();
			ПрофильОбъект = Выборка.Ссылка.ПолучитьОбъект();
			Если ПрофильОбъект <> Неопределено Тогда
				УдалитьРолиРасширений(ПрофильОбъект);
				Если ПрофильОбъект.Модифицированность() Тогда
					ОбновлениеИнформационнойБазы.ЗаписатьОбъект(ПрофильОбъект, Ложь);
					ЕстьИзменения = Истина;
				КонецЕсли;
			КонецЕсли;
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
	КонецЦикла;
	
	Если ЕстьИзменения Тогда
		РегистрыСведений.ТаблицыГруппДоступа.ОбновитьДанныеРегистра();
	КонецЕсли;
	
КонецПроцедуры

// Только для внутреннего использования.
// 
// Параметры:
//  ЭлементДанных - СправочникОбъект.ПрофилиГруппДоступа
//
Процедура ЗарегистрироватьПрофильИзмененныйПриЗагрузке(ЭлементДанных) Экспорт
	
	// Регистрация профилей, для групп доступа которых нужно обновить регистры сведений
	// ТаблицыГруппДоступа, ЗначенияГруппДоступа и ЗначенияГруппДоступаПоУмолчанию и роли пользователей.
	
	СтарыеЗначения = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(ЭлементДанных.Ссылка,
		"Ссылка, ПометкаУдаления, Роли, Назначение, ВидыДоступа, ЗначенияДоступа");
	
	ТребуетсяРегистрация = Ложь;
	Профиль = ЭлементДанных.Ссылка;
	ИзмененныеРоли = Неопределено;
	
	Если ТипЗнч(ЭлементДанных) = Тип("УдалениеОбъекта") Тогда
		Если СтарыеЗначения.Ссылка = Неопределено Тогда
			Возврат;
		КонецЕсли;
		ТребуетсяРегистрация = Истина;
		ИзмененныеРоли = ?(СтарыеЗначения.ПометкаУдаления, Новый Массив,
			СтарыеЗначения.Роли.Выгрузить().ВыгрузитьКолонку("Роль"));
		
	ИначеЕсли СтарыеЗначения.Ссылка <> ЭлементДанных.Ссылка Тогда
		ТребуетсяРегистрация = Истина;
		Профиль = ПользователиСлужебный.СсылкаОбъекта(ЭлементДанных);
		ИзмененныеРоли = ?(ЭлементДанных.ПометкаУдаления, Новый Массив,
			ЭлементДанных.Роли.ВыгрузитьКолонку("Роль"));
		
	ИначеЕсли СтарыеЗначения.ПометкаУдаления <> ЭлементДанных.ПометкаУдаления Тогда
		ТребуетсяРегистрация = Истина;
		ИзмененныеРоли = ?(ЭлементДанных.ПометкаУдаления,
			СтарыеЗначения.Роли.Выгрузить().ВыгрузитьКолонку("Роль"),
			ЭлементДанных.Роли.ВыгрузитьКолонку("Роль"));
		
	ИначеЕсли ИзменилисьВидыИлиЗначенияДоступаИлиНазначение(СтарыеЗначения, ЭлементДанных) Тогда
		ТребуетсяРегистрация = Истина;
	КонецЕсли;
	
	Если ИзмененныеРоли = Неопределено Тогда
		СтарыеРоли = СтарыеЗначения.Роли.Выгрузить(); // ТаблицаЗначений
		СтарыеРоли.Индексы.Добавить("Роль");
		НовыеРоли = ЭлементДанных.Роли.Выгрузить();
		НовыеРоли.Индексы.Добавить("Роль");
		ИзмененныеРоли = Новый Массив;
		Для Каждого Строка Из НовыеРоли Цикл
			Если СтарыеРоли.Найти(Строка.Роль, "Роль") = Неопределено Тогда
				ИзмененныеРоли.Добавить(Строка.Роль);
			КонецЕсли;
		КонецЦикла;
		Для Каждого Строка Из СтарыеРоли Цикл
			Если НовыеРоли.Найти(Строка.Роль, "Роль") = Неопределено Тогда
				ИзмененныеРоли.Добавить(Строка.Роль);
			КонецЕсли;
		КонецЦикла;
		Если ИзмененныеРоли.Количество() > 0 Тогда
			ТребуетсяРегистрация = Истина;
		КонецЕсли;
	КонецЕсли;
	
	Если Не ТребуетсяРегистрация Тогда
		Возврат;
	КонецЕсли;
	
	Справочники.ГруппыДоступа.ЗарегистрироватьСсылки("Профили", Профиль);
	
	Если УправлениеДоступомСлужебный.ОграничиватьДоступНаУровнеЗаписейУниверсально() Тогда
		Справочники.ГруппыДоступа.ЗарегистрироватьСсылки("Роли", ИзмененныеРоли);
	КонецЕсли;
	
КонецПроцедуры

// Только для внутреннего использования.
Процедура ОбновитьВспомогательныеДанныеПрофилейИзмененныхПриЗагрузке() Экспорт
	
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		// Изменения профилей в АРМ заблокированы и не загружаются в область данных.
		Возврат;
	КонецЕсли;
	
	ИзмененныеПрофили = Справочники.ГруппыДоступа.ЗарегистрированныеСсылки("Профили");
	
	Если ИзмененныеПрофили.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Если ИзмененныеПрофили.Количество() = 1
	   И ИзмененныеПрофили[0] = Неопределено Тогда
		
		ОбновитьВспомогательныеДанныеПрофилей();
	Иначе
		ОбновитьВспомогательныеДанныеПрофилей(ИзмененныеПрофили);
	КонецЕсли;
	
	Справочники.ГруппыДоступа.ЗарегистрироватьСсылки("Профили", Null);
	
	Если Не УправлениеДоступомСлужебный.ОграничиватьДоступНаУровнеЗаписейУниверсально() Тогда
		Возврат;
	КонецЕсли;
	
	ИзмененныеРоли = Справочники.ГруппыДоступа.ЗарегистрированныеСсылки("Роли");
	Если ИзмененныеРоли.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Если ИзмененныеРоли.Количество() = 1
	   И ИзмененныеРоли[0] = Неопределено Тогда
		
		ИзмененныеРоли = Неопределено;
	КонецЕсли;
	
	УправлениеДоступомСлужебный.ЗапланироватьОбновлениеДоступаПриИзмененииРолейПрофиля(
		"ОбновитьВспомогательныеДанныеПрофилейИзмененныхПриЗагрузке", ИзмененныеРоли);
	
	Справочники.ГруппыДоступа.ЗарегистрироватьСсылки("Роли", Null);
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Начальное заполнение

// Смотри также ОбновлениеИнформационнойБазыПереопределяемый.ПриНастройкеНачальногоЗаполненияЭлементов
// 
// Параметры:
//  Настройки - см. ОбновлениеИнформационнойБазыПереопределяемый.ПриНастройкеНачальногоЗаполненияЭлементов.Настройки
//
Процедура ПриНастройкеНачальногоЗаполненияЭлементов(Настройки) Экспорт
	
	Настройки.ПриНачальномЗаполненииЭлемента = Ложь;
	
КонецПроцедуры

// Смотри также ОбновлениеИнформационнойБазыПереопределяемый.ПриНачальномЗаполненииЭлементов
// 
// Параметры:
//   КодыЯзыков - см. ОбновлениеИнформационнойБазыПереопределяемый.ПриНачальномЗаполненииЭлементов.КодыЯзыков
//   Элементы - см. ОбновлениеИнформационнойБазыПереопределяемый.ПриНачальномЗаполненииЭлементов.Элементы
//   ТабличныеЧасти - см. ОбновлениеИнформационнойБазыПереопределяемый.ПриНачальномЗаполненииЭлементов.ТабличныеЧасти
//
Процедура ПриНачальномЗаполненииЭлементов(КодыЯзыков, Элементы, ТабличныеЧасти) Экспорт

	Элемент = Элементы.Добавить();
	Элемент.ИмяПредопределенныхДанных = "Администратор";
	Элемент.Наименование = НСтр("ru = 'Администратор'", ОбщегоНазначения.КодОсновногоЯзыка());
	Элемент.ИдентификаторПоставляемыхДанных =
		Новый УникальныйИдентификатор(ИдентификаторПрофиляАдминистратор());
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Вспомогательные процедуры и функции.

Функция ЗаполненныеПоставляемыеПрофили()
	
	ПараметрыОбновления = Новый Структура;
	// Свойства обновления поставляемых профилей.
	ПараметрыОбновления.Вставить("ОбновлятьИзмененныеПрофили", Истина);
	ПараметрыОбновления.Вставить("ЗапретитьИзменениеПрофилей", Истина);
	// Свойства обновления групп доступа поставляемых профилей.
	ПараметрыОбновления.Вставить("ОбновлятьГруппыДоступа", Истина);
	ПараметрыОбновления.Вставить("ОбновлятьГруппыДоступаСУстаревшимиНастройками", Ложь);
	
	ОписанияПрофилей = Новый Массив;
	
	// Описание для заполнения предопределенного профиля "Администратор".
	ОписаниеПрофиляАдминистратор = УправлениеДоступом.НовоеОписаниеПрофиляГруппДоступа();
	ЗаполнитьПрофильАдминистратор(ОписаниеПрофиляАдминистратор);
	ОписанияПрофилей.Добавить(ОписаниеПрофиляАдминистратор);
	
	ОписаниеПапкиПрофилей = УправлениеДоступом.НовоеОписаниеПапкиПрофилейГруппДоступа();
	ЗаполнитьПапкуПрофилейДополнительныеПрофили(ОписаниеПапкиПрофилей);
	ОписанияПрофилей.Добавить(ОписаниеПапкиПрофилей);
	
	ИнтеграцияПодсистемБСП.ПриЗаполненииПоставляемыхПрофилейГруппДоступа(
		ОписанияПрофилей, ПараметрыОбновления);
	
	УправлениеДоступомПереопределяемый.ПриЗаполненииПоставляемыхПрофилейГруппДоступа(
		ОписанияПрофилей, ПараметрыОбновления);
	
	Если ОписанияПрофилей.Найти(ОписаниеПрофиляАдминистратор) = Неопределено Тогда
		ОписанияПрофилей.Добавить(ОписаниеПрофиляАдминистратор);
	КонецЕсли;
	
	ЗаполнитьПрофильАдминистратор(ОписаниеПрофиляАдминистратор, Истина);
	
	Если Не ОбщегоНазначения.РазделениеВключено() Тогда
		ОписанияПрофилей.Добавить(
			УправлениеДоступомСлужебный.ОписаниеПрофиляОткрытиеВнешнихОтчетовИОбработок());
	КонецЕсли;
	
	Возврат Новый Структура("ОписанияПрофилей, ПараметрыОбновления", ОписанияПрофилей, ПараметрыОбновления);
	
КонецФункции

// Для функции ЗаполненныеПоставляемыеПрофили.
//
// Параметры:
//    ОписаниеПрофиляАдминистратор - см. УправлениеДоступом.НовоеОписаниеПрофиляГруппДоступа
//
Процедура ЗаполнитьПрофильАдминистратор(ОписаниеПрофиляАдминистратор, КромеОписания = Ложь)
	
	Если КромеОписания Тогда
		Описание = ОписаниеПрофиляАдминистратор.Описание;
	Иначе
		Описание =
			НСтр("ru = 'Предназначен для:
			           |- настройки параметров работы и обслуживания информационной системы,
			           |- настройки прав доступа других пользователей,
			           |- удаления помеченных объектов,
			           |- в редких случаях для внесения изменений в конфигурацию.
			           |
			           |Рекомендуется не использовать для обычной работы в информационной системе.'");
	КонецЕсли;
	
	ЗаполнитьЗначенияСвойств(ОписаниеПрофиляАдминистратор,
		УправлениеДоступом.НовоеОписаниеПрофиляГруппДоступа());
	
	ОписаниеПрофиляАдминистратор.Имя           = "Администратор";
	ОписаниеПрофиляАдминистратор.Идентификатор = ИдентификаторПрофиляАдминистратор();
	ОписаниеПрофиляАдминистратор.Наименование  = НСтр("ru = 'Администратор'", ОбщегоНазначения.КодОсновногоЯзыка());
	ОписаниеПрофиляАдминистратор.Роли.Добавить("АдминистраторСистемы");
	ОписаниеПрофиляАдминистратор.Роли.Добавить("ПолныеПрава");
	ОписаниеПрофиляАдминистратор.Описание = Описание;
	
КонецПроцедуры

// Для функции ЗаполненныеПоставляемыеПрофили.
//
// Параметры:
//    ОписаниеПрофиляАдминистратор - см. УправлениеДоступом.НовоеОписаниеПапкиПрофилейГруппДоступа
//
Процедура ЗаполнитьПапкуПрофилейДополнительныеПрофили(ОписаниеПапки)
	
	ОписаниеПапки = УправлениеДоступом.НовоеОписаниеПапкиПрофилейГруппДоступа();
	ОписаниеПапки.Имя           = "ДополнительныеПрофили";
	ОписаниеПапки.Идентификатор = "69a066e7-ce81-11eb-881c-b06ebfbf08c7";
	ОписаниеПапки.Наименование  = НСтр("ru = 'Дополнительные профили'", ОбщегоНазначения.КодОсновногоЯзыка());
	
КонецПроцедуры

// Для процедуры УправлениеДоступомСлужебныйПовтИсп.ОписаниеПоставляемыхПрофилейСеанса.
// Смотри также УправлениеДоступомПереопределяемый.ПриЗаполненииПоставляемыхПрофилейГруппДоступа.
//
// Параметры:
//  СвойстваВидовДоступа - см. УправлениеДоступомСлужебный.СвойстваВидовДоступа
//                       - Неопределено
//  ХешСумма - Строка - возвращаемое значение.
//
// Возвращаемое значение:
//   см. УправлениеДоступомСлужебный.ПоставляемыеПрофили
//
Функция ПроверенныеПоставляемыеПрофилиСеанса(СвойстваВидовДоступа = Неопределено, ХешСумма = "") Экспорт
	
	ЗаполненныеПоставляемыеПрофили = ЗаполненныеПоставляемыеПрофили();
	ПараметрыОбновления = ЗаполненныеПоставляемыеПрофили.ПараметрыОбновления;
	ОписанияПрофилей    = ЗаполненныеПоставляемыеПрофили.ОписанияПрофилей; // Массив из см. УправлениеДоступом.НовоеОписаниеПрофиляГруппДоступа
	
	ЗаголовокОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Заданы недопустимые значения в процедуре %1
		           |общего модуля %2.'"),
		"ПриЗаполненииПоставляемыхПрофилейГруппДоступа",
		"УправлениеДоступомПереопределяемый")
		+ Символы.ПС
		+ Символы.ПС;
	
	Если ПараметрыОбновления.ЗапретитьИзменениеПрофилей
	   И НЕ ПараметрыОбновления.ОбновлятьИзмененныеПрофили Тогда
		
		ТекстОшибки = ЗаголовокОшибки +  СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Когда в параметре %1 свойство
			           |%2 установлено Ложь,
			           |тогда свойство %3 тоже
			           |должно быть установлено Ложь.'"),
			"ПараметрыОбновления",
			"ОбновлятьИзмененныеПрофили",
			"ЗапретитьИзменениеПрофилей");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	ВсеРоли = ПользователиСлужебный.ВсеРоли().Соответствие;
	Если СвойстваВидовДоступа = Неопределено Тогда
		СвойстваВидовДоступа = УправлениеДоступомСлужебный.СвойстваВидовДоступа();
	КонецЕсли;
	
	// Преобразование описаний в соответствие идентификаторов и
	// свойств для хранения и быстрой обработки.
	ВсеИмена           = Новый Соответствие;
	ВсеИдентификаторы  = Новый Соответствие;
	ИменаПапокПрофилей = Новый Соответствие;
	СписокПрофилей     = Новый СписокЗначений;
	
	Для Каждого ОписаниеПрофиля Из ОписанияПрофилей Цикл
		ОписаниеПрофиля.Удалить("Описание");
		ЭтоГруппа = Не ОписаниеПрофиля.Свойство("Роли");
		
		Если Не ЗначениеЗаполнено(ОписаниеПрофиля.Идентификатор) Тогда
			ШаблонОшибки = ?(ЭтоГруппа,
				НСтр("ru = 'В описании папки профилей ""%1"" не заполнено свойство %2.'"),
				НСтр("ru = 'В описании профиля ""%1"" не заполнено свойство %2.'"));
			ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонОшибки,
				?(ЗначениеЗаполнено(ОписаниеПрофиля.Имя), ОписаниеПрофиля.Имя, ОписаниеПрофиля.Наименование),
				"Идентификатор");
			ВызватьИсключение ТекстОшибки;
		ИначеЕсли Не СтроковыеФункцииКлиентСервер.ЭтоУникальныйИдентификатор(ОписаниеПрофиля.Идентификатор) Тогда
			ШаблонОшибки = ?(ЭтоГруппа,
				НСтр("ru = 'В описании папки профилей ""%1"" указан некорректный идентификатор: ""%2"".'"),
				НСтр("ru = 'В описании профиля ""%1"" указан некорректный идентификатор: ""%2"".'"));
			ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонОшибки,
				?(ЗначениеЗаполнено(ОписаниеПрофиля.Имя), ОписаниеПрофиля.Имя, ОписаниеПрофиля.Наименование),
				ОписаниеПрофиля.Идентификатор);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		
		СвойстваПрофиля = Новый Структура;
		СвойстваПрофиля.Вставить("Имя",           "");
		СвойстваПрофиля.Вставить("Родитель",      "");
		СвойстваПрофиля.Вставить("Идентификатор", "");
		СвойстваПрофиля.Вставить("Наименование",  "");
		ЗаполнитьЗначенияСвойств(СвойстваПрофиля, ОписаниеПрофиля);
		СвойстваПрофиля.Вставить("ЭтоГруппа", ЭтоГруппа);
		
		Если ВсеИдентификаторы.Получить(ВРег(СвойстваПрофиля.Идентификатор)) <> Неопределено Тогда
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Значение ""%1"" свойства %2 уже используется в описании другого профиля или папки профилей.'"),
				СвойстваПрофиля.Идентификатор, "Идентификатор");
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		ВсеИдентификаторы.Вставить(ВРег(СвойстваПрофиля.Идентификатор), Истина);
		СписокПрофилей.Добавить(СвойстваПрофиля, НРег(СвойстваПрофиля.Идентификатор));
		
		Если ЭтоГруппа И Не ЗначениеЗаполнено(СвойстваПрофиля.Имя) Тогда
			ТекстОшибки =  ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'В описании папки профилей ""%1"" не заполнено свойство %2.'"),
				?(ЗначениеЗаполнено(ОписаниеПрофиля.Наименование), ОписаниеПрофиля.Наименование, ОписаниеПрофиля.Идентификатор),
				"Имя");
			ВызватьИсключение ТекстОшибки;
		ИначеЕсли ЗначениеЗаполнено(СвойстваПрофиля.Имя) Тогда
			Если СокрЛП(СвойстваПрофиля.Имя) <> СвойстваПрофиля.Имя Тогда
				ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Значение ""%1"" свойства %2 профиля или папки профилей содержит непечатаемые символы.'"),
					СвойстваПрофиля.Имя, "Имя");
				ВызватьИсключение ТекстОшибки;
			КонецЕсли;
			Если ВсеИмена.Получить(ВРег(СвойстваПрофиля.Имя)) <> Неопределено Тогда
				ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Значение ""%1"" свойства %2 уже используется в описании другого профиля или папки профилей.'"),
					СвойстваПрофиля.Имя, "Имя");
				ВызватьИсключение ТекстОшибки;
			КонецЕсли;
			ВсеИмена.Вставить(ВРег(СвойстваПрофиля.Имя), Истина);
		КонецЕсли;
		
		Если ЭтоГруппа Тогда
			ИменаПапокПрофилей.Вставить(ВРег(СвойстваПрофиля.Имя), СвойстваПрофиля.Родитель);
			Продолжить;
		КонецЕсли;
		
		ПодготовитьНазначениеПоставляемогоПрофиля(СвойстваПрофиля, ОписаниеПрофиля, ЗаголовокОшибки);
		
		НазначениеПрофиля = УправлениеДоступомСлужебныйКлиентСервер.НазначениеПрофиля(СвойстваПрофиля);
		ПодготовитьРолиПоставляемогоПрофиля(СвойстваПрофиля, ОписаниеПрофиля, НазначениеПрофиля, ВсеРоли, ЗаголовокОшибки);
		
		ПодготовитьВидыДоступаПоставляемогоПрофиля(СвойстваПрофиля, ОписаниеПрофиля, НазначениеПрофиля, СвойстваВидовДоступа, ЗаголовокОшибки);
		ПодготовитьЗначенияДоступаПоставляемогоПрофиля(СвойстваПрофиля, ОписаниеПрофиля, СвойстваВидовДоступа, ЗаголовокОшибки);
	КонецЦикла;
	
	Для Каждого ОписаниеПрофиля Из ОписанияПрофилей Цикл
		Если Не ЗначениеЗаполнено(ОписаниеПрофиля.Родитель) Тогда
			Продолжить;
		КонецЕсли;
		Если ИменаПапокПрофилей.Получить(ВРег(ОписаниеПрофиля.Родитель)) <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		ЭтоГруппа = Не ОписаниеПрофиля.Свойство("Роли");
		ШаблонОшибки = ?(ЭтоГруппа,
			НСтр("ru = 'В описании папки профилей ""%1"" в свойстве %2
			           |указано несуществующее имя папки профилей ""%3"".'"),
			НСтр("ru = 'В описании профиля ""%1"" в свойстве %2
			           |указано несуществующее имя папки профилей ""%3"".'"));
		ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонОшибки,
			?(ЗначениеЗаполнено(ОписаниеПрофиля.Имя), ОписаниеПрофиля.Имя, ОписаниеПрофиля.Наименование),
			"Родитель", ОписаниеПрофиля.Родитель);
		ВызватьИсключение ТекстОшибки;
	КонецЦикла;
	
	ПапкиПоРодителям = Новый Соответствие;
	Для Каждого ОписаниеПрофиля Из ОписанияПрофилей Цикл
		ЭтоГруппа = Не ОписаниеПрофиля.Свойство("Роли");
		Если Не ЭтоГруппа Тогда
			Продолжить;
		КонецЕсли;
		ПапкиРодителя = ПапкиПоРодителям.Получить(ОписаниеПрофиля.Родитель);
		Если ПапкиРодителя = Неопределено Тогда
			ПапкиРодителя = Новый Соответствие;
			ПапкиПоРодителям.Вставить(ОписаниеПрофиля.Родитель, ПапкиРодителя);
		КонецЕсли;
		ПапкиРодителя.Вставить(ОписаниеПрофиля.Имя, Истина);
		Если Не ЗначениеЗаполнено(ОписаниеПрофиля.Родитель) Тогда
			Продолжить;
		КонецЕсли;
		РодителиПапки = Новый Массив;
		Родитель = ОписаниеПрофиля.Родитель;
		Пока Истина Цикл
			РодителиПапки.Добавить(Родитель);
			Родитель = ИменаПапокПрофилей.Получить(ВРег(Родитель));
			Если Не ЗначениеЗаполнено(Родитель) Тогда
				Прервать;
			КонецЕсли;
			Если РодителиПапки.Найти(Родитель) = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'В описании папки профилей ""%1"" в свойстве %2
				           |указано имя папки профилей ""%3"",
				           |создающее циклическую зависимость: ""%4"".'"),
				?(ЗначениеЗаполнено(ОписаниеПрофиля.Имя), ОписаниеПрофиля.Имя, ОписаниеПрофиля.Наименование),
				"Родитель",
				ОписаниеПрофиля.Родитель,
				СтрСоединить(РодителиПапки, " -> "));
			ВызватьИсключение ТекстОшибки;
		КонецЦикла;
	КонецЦикла;
	
	ОписаниеВерсии = Новый Структура("СвойстваВерсии", Новый Массив);
	ДобавитьЭлементВерсии(ОписаниеВерсии, "Версия", "1");
	ДобавитьЭлементВерсии(ОписаниеВерсии, "ИменаПредопределенных",
		Метаданные.Справочники.ПрофилиГруппДоступа.ПолучитьИменаПредопределенных());
	
	ДобавитьСвойстваВерсии(ОписаниеВерсии, ПараметрыОбновления,
		"ОбновлятьИзмененныеПрофили,
		|ЗапретитьИзменениеПрофилей,
		|ОбновлятьГруппыДоступа,
		|ОбновлятьГруппыДоступаСУстаревшимиНастройками");
	
	СписокПрофилей.СортироватьПоПредставлению();
	СвойстваПрофилей = Новый Соответствие;
	ОписанияПрофилейМассив = Новый Массив;
	РазделениеВключено = ОбщегоНазначения.РазделениеВключено();
	
	Для Каждого ОписаниеПрофиля Из СписокПрофилей Цикл
		СвойстваПрофиля = Новый ФиксированнаяСтруктура(ОписаниеПрофиля.Значение);
		ОписанияПрофилейМассив.Добавить(СвойстваПрофиля);
		СвойстваПрофилей.Вставить(СвойстваПрофиля.Идентификатор, СвойстваПрофиля);
		Если ЗначениеЗаполнено(СвойстваПрофиля.Имя) Тогда
			СвойстваПрофилей.Вставить(СвойстваПрофиля.Имя, СвойстваПрофиля);
		КонецЕсли;
		ОписаниеВерсии.СвойстваВерсии.Добавить("");
		ДобавитьСвойстваВерсии(ОписаниеВерсии, СвойстваПрофиля,
			"Имя, Родитель, Идентификатор, Наименование");
		Если СвойстваПрофиля.ЭтоГруппа Тогда
			Продолжить;
		КонецЕсли;
		ДобавитьСвойстваВерсии(ОписаниеВерсии, СвойстваПрофиля, "Назначение, Роли");
		Если РазделениеВключено Тогда
			РолиНедоступныеВСервисе = Новый СписокЗначений;
			Для Каждого ОписаниеРоли Из СвойстваПрофиля.РолиНедоступныеВСервисе Цикл
				РолиНедоступныеВСервисе.Добавить(ОписаниеРоли.Ключ);
			КонецЦикла;
			РолиНедоступныеВСервисе.СортироватьПоЗначению();
			ДобавитьЭлементВерсии(ОписаниеВерсии,
				"РолиНедоступныеВСервисе", РолиНедоступныеВСервисе.ВыгрузитьЗначения());
		КонецЕсли;
		ВидыДоступа = Новый СписокЗначений;
		Для Каждого КлючИЗначение Из СвойстваПрофиля.ВидыДоступа Цикл
			ЗначенияДоступа = Новый СписокЗначений;
			ВидыДоступа.Добавить(ЗначенияДоступа, КлючИЗначение.Ключ + " (" + КлючИЗначение.Значение + ")");
			Для Каждого ОписаниеЗначения Из СвойстваПрофиля.ЗначенияДоступа Цикл
				Если ОписаниеЗначения.ВидДоступа = КлючИЗначение.Ключ Тогда
					ЗначенияДоступа.Добавить(ОписаниеЗначения.ЗначениеДоступа);
				КонецЕсли;
			КонецЦикла;
			ЗначенияДоступа.СортироватьПоЗначению();
		КонецЦикла;
		ВидыДоступа.СортироватьПоПредставлению();
		Для Каждого ЭлементСписка Из ВидыДоступа Цикл
			Если ЗначениеЗаполнено(ЭлементСписка.Значение) Тогда
				ДобавитьЭлементВерсии(ОписаниеВерсии,
					ЭлементСписка.Представление, ЭлементСписка.Значение.ВыгрузитьЗначения());
			Иначе
				ОписаниеВерсии.СвойстваВерсии.Добавить(ЭлементСписка.Представление);
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
	СтрокаВерсии = СтрСоединить(ОписаниеВерсии.СвойстваВерсии, Символы.ПС);
	ХешСумма = УправлениеДоступомСлужебный.ХешСуммаДанных(СтрокаВерсии);
	
	Результат = Новый Структура;
	Результат.Вставить("ПараметрыОбновления",
		Новый ФиксированнаяСтруктура(ПараметрыОбновления));
	
	Результат.Вставить("ОписанияПрофилей",
		Новый ФиксированноеСоответствие(СвойстваПрофилей));
	
	Результат.Вставить("ОписанияПрофилейМассив",
		Новый ФиксированныйМассив(ОписанияПрофилейМассив));
	
	Результат.Вставить("ПапкиПоРодителям",
		ОбщегоНазначения.ФиксированныеДанные(ПапкиПоРодителям));
	
	Возврат Новый ФиксированнаяСтруктура(Результат);
	
КонецФункции

// Для функции ПоставляемыеПрофили.
Процедура ПодготовитьНазначениеПоставляемогоПрофиля(СвойстваПрофиля, ОписаниеПрофиля, ЗаголовокОшибки)
	
	Если ОписаниеПрофиля.Назначение.Количество() = 0 Тогда
		ОписаниеПрофиля.Назначение.Добавить(Тип("СправочникСсылка.Пользователи"));
	КонецЕсли;
	СписокНазначений = Новый СписокЗначений;
	Для Каждого Тип Из ОписаниеПрофиля.Назначение Цикл
		Если ТипЗнч(Тип) = Тип("ОписаниеТипов") Тогда
			Типы = Тип.Типы();
		Иначе
			Типы = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(Тип);
		КонецЕсли;
		Для Каждого Тип Из Типы Цикл
			Если ТипЗнч(Тип) <> Тип("Тип")
			 Или Не ОбщегоНазначения.ЭтоСсылка(Тип)
			 Или Не Метаданные.ОпределяемыеТипы.Пользователь.Тип.СодержитТип(Тип)
			 Или Тип <> Тип("СправочникСсылка.Пользователи")
			   И Не Метаданные.ОпределяемыеТипы.ВнешнийПользователь.Тип.СодержитТип(Тип) Тогда
				ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'В описании профиля ""%1""
					           |указано недопустимое назначение ""%2 (%3)"".
					           |Ожидается назначение, как значение типа ""%4"" для ссылки,
					           |указанное в определяемом типе %5 и
					           |указанное в определяемом типе %6
					           |(кроме типа %7).'"),
					?(ЗначениеЗаполнено(ОписаниеПрофиля.Имя),
						ОписаниеПрофиля.Имя, ОписаниеПрофиля.Идентификатор),
					Строка(Тип),
					Строка(ТипЗнч(Тип)),
					"Тип",
					"Пользователь",
					"ВнешнийПользователь",
					"СправочникСсылка.Пользователи");
				ВызватьИсключение ТекстОшибки;
			КонецЕсли;
			ОписаниеТипаСсылки = Новый ОписаниеТипов(ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(Тип));
			Значение = ОписаниеТипаСсылки.ПривестиЗначение(Неопределено);
			СписокНазначений.Добавить(Значение);
		КонецЦикла;
	КонецЦикла;
	
	СписокНазначений.СортироватьПоЗначению();
	СвойстваПрофиля.Вставить("Назначение",
		Новый ФиксированныйМассив(СписокНазначений.ВыгрузитьЗначения()));
	
КонецПроцедуры

// Для функции ПоставляемыеПрофили.
Процедура ПодготовитьРолиПоставляемогоПрофиля(СвойстваПрофиля, ОписаниеПрофиля,
			НазначениеПрофиля, ВсеРоли, ЗаголовокОшибки)
	
	НедоступныеРоли = ПользователиСлужебныйПовтИсп.НедоступныеРоли(НазначениеПрофиля, Ложь);
	ПроверенныеРоли = Новый Соответствие;
	ИндексРоли = ОписаниеПрофиля.Роли.Количество();
	Пока ИндексРоли > 0 Цикл
		ИндексРоли = ИндексРоли - 1;
		Роль = ОписаниеПрофиля.Роли[ИндексРоли];
		// Проверка наличия ролей в метаданных.
		Если ВсеРоли.Получить(Роль) = Неопределено Тогда
			ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'В описании профиля ""%1 (%2)""
				           |указана несуществующая роль ""%3"".'"),
				ОписаниеПрофиля.Имя,
				ОписаниеПрофиля.Идентификатор,
				Роль);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		// Удаление дублей ролей.
		Если ПроверенныеРоли.Получить(ВРег(Роль)) <> Неопределено Тогда
			ОписаниеПрофиля.Роли.Удалить(ИндексРоли);
			Продолжить;
		КонецЕсли;
		ПроверенныеРоли.Вставить(ВРег(Роль), Истина);
		// Проверка соответствия назначения ролей и профиля.
		Если НедоступныеРоли.Получить(Роль) <> Неопределено Тогда
			ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'В описании профиля ""%1 (%2)""
				           |указана роль ""%3"", 
				           |которая не соответствует назначению профиля:
				           |""%4"".'"),
				ОписаниеПрофиля.Имя,
				ОписаниеПрофиля.Идентификатор,
				Роль,
				ПредставлениеНазначенияПрофиля(НазначениеПрофиля));
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
	КонецЦикла;
	
	ЗаполнитьСтандартныеРолиРасширений(ОписаниеПрофиля.Роли,
		УправлениеДоступомСлужебныйПовтИсп.ОписаниеСтандартныхРолейРасширенийСеанса().РолиСеанса);
	
	СписокРолей = Новый СписокЗначений;
	СписокРолей.ЗагрузитьЗначения(ОписаниеПрофиля.Роли);
	СписокРолей.СортироватьПоЗначению();
	СвойстваПрофиля.Вставить("Роли",
		Новый ФиксированныйМассив(СписокРолей.ВыгрузитьЗначения()));
	
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		// Заполнение списка недоступных ролей в модели сервиса
		// для определения необходимости обновления поставляемых профилей.
		СвойстваПрофиля.Вставить("РолиНедоступныеВСервисе",
			РолиПрофиляНедоступныеВСервисе(ОписаниеПрофиля, НазначениеПрофиля));
	КонецЕсли;
	
КонецПроцедуры

// Для функции ПоставляемыеПрофили.
Процедура ПодготовитьВидыДоступаПоставляемогоПрофиля(СвойстваПрофиля, ОписаниеПрофиля,
			НазначениеПрофиля, СвойстваВидовДоступа, ЗаголовокОшибки)
	
	ВидыДоступа = Новый Соответствие;
	Для Каждого ЭлементСписка Из ОписаниеПрофиля.ВидыДоступа Цикл
		ИмяВидаДоступа       = ЭлементСписка.Значение;
		УточнениеВидаДоступа = ЭлементСписка.Представление;
		Если СвойстваВидовДоступа.ПоИменам.Получить(ИмяВидаДоступа) = Неопределено Тогда
			ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'В описании профиля ""%1""
				           |указан несуществующий вид доступа ""%2"".'"),
				?(ЗначениеЗаполнено(ОписаниеПрофиля.Имя),
				  ОписаниеПрофиля.Имя,
				  ОписаниеПрофиля.Идентификатор),
				ИмяВидаДоступа);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		
		ВидДоступаСоответствуетНазначениюПрофиля =
			УправлениеДоступомСлужебныйКлиентСервер.ВидДоступаСоответствуетНазначениюПрофиля(
				ИмяВидаДоступа, НазначениеПрофиля);
		
		Если Не ВидДоступаСоответствуетНазначениюПрофиля Тогда
			ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'В описании профиля ""%1""
				           |указан вид доступа ""%2""
				           |который не соответствует назначению профиля:
				           |""%3"".'"),
				?(ЗначениеЗаполнено(ОписаниеПрофиля.Имя),
				  ОписаниеПрофиля.Имя,
				  ОписаниеПрофиля.Идентификатор),
				ИмяВидаДоступа,
				ПредставлениеНазначенияПрофиля(НазначениеПрофиля));
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		Если УточнениеВидаДоступа <> ""
		   И УточнениеВидаДоступа <> "ВначалеВсеЗапрещены"
		   И УточнениеВидаДоступа <> "Предустановленный"
		   И УточнениеВидаДоступа <> "ВначалеВсеРазрешены" Тогда
			
			ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'В описании профиля ""%1""
				           |для вида доступа ""%2"" указано неизвестное уточнение ""%3"".
				           |
				           |Допустимы только следующие уточнения:
				           |- ""%4"" или """",
				           |- ""%5"",
				           |- ""%6"".'"),
				?(ЗначениеЗаполнено(ОписаниеПрофиля.Имя),
					ОписаниеПрофиля.Имя,
					ОписаниеПрофиля.Идентификатор),
				ИмяВидаДоступа,
				УточнениеВидаДоступа,
				"ВначалеВсеЗапрещены",
				"ВначалеВсеРазрешены",
				"Предустановленный");
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		ВидыДоступа.Вставить(ИмяВидаДоступа, УточнениеВидаДоступа);
	КонецЦикла;
	СвойстваПрофиля.Вставить("ВидыДоступа", Новый ФиксированноеСоответствие(ВидыДоступа));
	
КонецПроцедуры

// Для функции ПоставляемыеПрофили.
Процедура ПодготовитьЗначенияДоступаПоставляемогоПрофиля(СвойстваПрофиля, ОписаниеПрофиля, СвойстваВидовДоступа, ЗаголовокОшибки);
	
	// Удаление повторяющихся значений.
	ЗначенияДоступа = Новый Массив;
	ТаблицаЗначенийДоступа = Новый ТаблицаЗначений;
	ТаблицаЗначенийДоступа.Колонки.Добавить("ВидДоступа",      Метаданные.ОпределяемыеТипы.ЗначениеДоступа.Тип);
	ТаблицаЗначенийДоступа.Колонки.Добавить("ЗначениеДоступа", Метаданные.ОпределяемыеТипы.ЗначениеДоступа.Тип);
	
	Для Каждого ЭлементСписка Из ОписаниеПрофиля.ЗначенияДоступа Цикл
		Отбор = Новый Структура;
		Отбор.Вставить("ВидДоступа",      ЭлементСписка.Значение);
		Отбор.Вставить("ЗначениеДоступа", ЭлементСписка.Представление);
		ВидДоступа      = Отбор.ВидДоступа;
		ЗначениеДоступа = Отбор.ЗначениеДоступа;
		
		СвойстваВидаДоступа = СвойстваВидовДоступа.ПоИменам.Получить(ВидДоступа);
		Если СвойстваВидаДоступа = Неопределено Тогда
			ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'В описании профиля ""%1""
				           |указан несуществующий вид доступа ""%2""
				           |для значения доступа
				           |""%3"".'"),
				?(ЗначениеЗаполнено(ОписаниеПрофиля.Имя),
				  ОписаниеПрофиля.Имя,
				  ОписаниеПрофиля.Идентификатор),
				ВидДоступа,
				ЗначениеДоступа);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		
		ОбъектМетаданных = Неопределено;
		ПозицияТочки = СтрНайти(ЗначениеДоступа, ".");
		Если ПозицияТочки > 0 Тогда
			ВидОбъектаМетаданных = Лев(ЗначениеДоступа, ПозицияТочки - 1);
			ОстатокСтроки = Сред(ЗначениеДоступа, ПозицияТочки + 1);
			ПозицияТочки = СтрНайти(ОстатокСтроки, ".");
			Если ПозицияТочки > 0 Тогда
				ИмяОбъектаМетаданных = Лев(ОстатокСтроки, ПозицияТочки - 1);
				ПолноеИмяОбъектаМетаданных = ВидОбъектаМетаданных + "." + ИмяОбъектаМетаданных;
				ОбъектМетаданных = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмяОбъектаМетаданных);
			КонецЕсли;
		КонецЕсли;
		
		Если ОбъектМетаданных = Неопределено Тогда
			ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'В описании профиля ""%1""
				           |для вида доступа ""%2""
				           |не существует тип указанного значения доступа
				           |""%3"".'"),
				?(ЗначениеЗаполнено(ОписаниеПрофиля.Имя),
				  ОписаниеПрофиля.Имя,
				  ОписаниеПрофиля.Идентификатор),
				ВидДоступа,
				ЗначениеДоступа);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		
		Попытка
			ПустаяСсылкаЗначенияДоступа = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(
				ПолноеИмяОбъектаМетаданных).ПустаяСсылка();
		Исключение
			ПустаяСсылкаЗначенияДоступа = Неопределено;
		КонецПопытки;
		
		Если ПустаяСсылкаЗначенияДоступа = Неопределено Тогда
			ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'В описании профиля ""%1""
				           |для вида доступа ""%2""
				           |указан не ссылочный тип значения доступа
				           |""%3"".'"),
				?(ЗначениеЗаполнено(ОписаниеПрофиля.Имя),
				  ОписаниеПрофиля.Имя,
				  ОписаниеПрофиля.Идентификатор),
				ВидДоступа,
				ЗначениеДоступа);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		ТипЗначенияДоступа = ТипЗнч(ПустаяСсылкаЗначенияДоступа);
		
		СвойстваВидаДоступаПоТипу = СвойстваВидовДоступа.ПоТипамЗначений.Получить(ТипЗначенияДоступа); // См. УправлениеДоступомСлужебный.СвойстваВидаДоступа
		Если СвойстваВидаДоступаПоТипу = Неопределено
		 ИЛИ СвойстваВидаДоступаПоТипу.Имя <> ВидДоступа Тогда
			
			ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'В описании профиля ""%1""
				           |указано значение доступа ""%3""
				           |типа, который не указан в свойствах вида доступа ""%2"".'"),
				?(ЗначениеЗаполнено(ОписаниеПрофиля.Имя),
				  ОписаниеПрофиля.Имя,
				  ОписаниеПрофиля.Идентификатор),
				ВидДоступа,
				ЗначениеДоступа);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		
		Если ТаблицаЗначенийДоступа.НайтиСтроки(Отбор).Количество() > 0 Тогда
			ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'В описании профиля ""%1""
				           |для вида доступа ""%2""
				           |повторно указано значение доступа
				           |""%3"".'"),
				?(ЗначениеЗаполнено(ОписаниеПрофиля.Имя),
				  ОписаниеПрофиля.Имя,
				  ОписаниеПрофиля.Идентификатор),
				ВидДоступа,
				ЗначениеДоступа);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		ЗначенияДоступа.Добавить(Новый ФиксированнаяСтруктура(Отбор));
	КонецЦикла;
	СвойстваПрофиля.Вставить("ЗначенияДоступа", Новый ФиксированныйМассив(ЗначенияДоступа));

КонецПроцедуры

// Возвращает свойства профиля, указанные в переопределяемом модуле,
// преобразованные в фиксированный формат сохранения в базе данных.
// Подробное описание свойств смотри в функциях НовоеОписаниеПрофиляГруппДоступа
// и НовоеОписаниеПапкиПрофилейГруппДоступа общего модуля УправлениеДоступом.
// 
// Параметры:
//   Идентификатор - Строка - имя или идентификатор поставляемого профиля.
// 
// Возвращаемое значение:
//   ФиксированнаяСтруктура:
//     * Имя - Строка
//     * Идентификатор - Строка
//     * ЭтоГруппа     - Булево
//     * Наименование  - Строка
//     * Роли          - ФиксированныйМассив из Строка
//     * Назначение    - ФиксированныйМассив из ОпределяемыйТип.Пользователь
//     * ВидыДоступа   - ФиксированноеСоответствие из КлючИЗначение:
//          * Ключ - Строка
//          * Значение - Строка
//     * ЗначенияДоступа - ФиксированныйМассив из ФиксированнаяСтруктура:
//          * ВидДоступа - Строка
//          * ЗначениеДоступа - Строка
//   
//   Неопределено - профиль не существует.
//
Функция СвойстваПоставляемогоПрофиля(Идентификатор) Экспорт
	
	ПоставляемыеПрофили = УправлениеДоступомСлужебный.ПоставляемыеПрофили();
	
	Возврат ПоставляемыеПрофили.ОписанияПрофилей.Получить(Идентификатор);
	
КонецФункции

// Возвращаемое значение:
//   ФиксированноеСоответствие из КлючИЗначение:
//     * Ключ - Строка - строковый идентификатор поставляемого профиля
//     * Значение - Строка - описание поставляемого профиля
//
Функция ПояснениеПоставляемыхПрофилей() Экспорт
	
	ОписанияПрофилей = ЗаполненныеПоставляемыеПрофили().ОписанияПрофилей;
	
	ПредставлениеВидовДоступа = Новый Соответствие;
	
	Для Каждого ОписаниеПрофиля Из ОписанияПрофилей Цикл
		Описание = ?(ОписаниеПрофиля.Свойство("Описание"), ОписаниеПрофиля.Описание, "");
		ПредставлениеВидовДоступа.Вставить(ОписаниеПрофиля.Идентификатор, Описание);
	КонецЦикла;
	
	Возврат Новый ФиксированноеСоответствие(ПредставлениеВидовДоступа);
	
КонецФункции

// Для процедуры ПоставляемыеПрофили.
Функция ПредставлениеНазначенияПрофиля(НазначениеПрофиля)
	
	Если НазначениеПрофиля = "СовместноДляПользователейИВнешнихПользователей" Тогда
		Возврат НСтр("ru = 'Совместно для пользователей и внешних пользователей'");
		
	ИначеЕсли НазначениеПрофиля = "ДляВнешнихПользователей" Тогда
		Возврат НСтр("ru = 'Для внешних пользователей'");
	КонецЕсли;
	
	Возврат НСтр("ru = 'Для пользователей'");
	
КонецФункции

// Для процедуры ОбновитьСоставПредопределенныхПрофилей.
Функция ПредопределенныеПрофилиСовпадают(НовыеПрофили, СтарыеПрофили, Удаленные)
	
	Если ТипЗнч(НовыеПрофили) <> ТипЗнч(СтарыеПрофили) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ПредопределенныеПрофилиСовпадают =
		НовыеПрофили.Количество() = СтарыеПрофили.Количество();
	
	Для Каждого Профиль Из СтарыеПрофили Цикл
		Если НовыеПрофили.Найти(Профиль) = Неопределено Тогда
			ПредопределенныеПрофилиСовпадают = Ложь;
			Удаленные.Добавить(Профиль);
		КонецЕсли;
	КонецЦикла;
	
	Возврат ПредопределенныеПрофилиСовпадают;
	
КонецФункции

// Для процедуры ОбновитьПоставляемыеПрофили.
Процедура ОбновитьПоставляемыеПапкиПрофилей(Родитель, ТекущиеПапкиПрофилей, ПоставляемыеПрофили, Удаленные, ЕстьИзменения)
	
	ПапкиРодителя = ПоставляемыеПрофили.ПапкиПоРодителям.Получить(Родитель);
	Если Не ЗначениеЗаполнено(ПапкиРодителя) Тогда
		Возврат;
	КонецЕсли;
	
	Для Каждого КлючИЗначение Из ПапкиРодителя Цикл
		СвойстваПрофиля = ПоставляемыеПрофили.ОписанияПрофилей.Получить(КлючИЗначение.Ключ); // см. Справочники.ПрофилиГруппДоступа.СвойстваПоставляемогоПрофиля
		
		Идентификатор = Новый УникальныйИдентификатор(СвойстваПрофиля.Идентификатор);
		СтрокаТекущейПапки = ТекущиеПапкиПрофилей.Найти(Идентификатор, "ИдентификаторПоставляемыхДанных");
		Если СтрокаТекущейПапки <> Неопределено Тогда
			СтрокаТекущейПапки.Найден = Истина;
		КонецЕсли;
		// @skip-check query-in-loop - Порционная обработка данных в транзакции
		Если ОбновитьПрофильИлиПапкуПрофилей(СвойстваПрофиля, Удаленные, Истина) Тогда
			ЕстьИзменения = Истина;
		КонецЕсли;
		// @skip-check query-in-loop - Порционная обработка данных в транзакции
		ОбновитьПоставляемыеПапкиПрофилей(КлючИЗначение.Ключ,
			ТекущиеПапкиПрофилей, ПоставляемыеПрофили, Удаленные, ЕстьИзменения);
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ОбновитьПоставляемыеПрофили.
Процедура ПометитьНаУдалениеУстаревшиеПоставляемыеДанные(ПапкиИлиПрофили, ЕстьИзменения, ОбновленныеПрофили = Неопределено)
	
	Если Не Справочники.ВерсииРасширений.ВсеРасширенияПодключены() Тогда
		Возврат;
	КонецЕсли;
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("Справочник.ПрофилиГруппДоступа");
	
	Для Каждого СтрокаТекущегоПрофиля Из ПапкиИлиПрофили Цикл
		Если СтрокаТекущегоПрофиля.Найден Тогда
			Продолжить;
		КонецЕсли;
		Если Не СтрокаТекущегоПрофиля.ЭтоГруппа
		   И СтрокаТекущегоПрофиля.ПоставляемыйПрофильИзменен Тогда
			Продолжить;
		КонецЕсли;
		ЭлементБлокировки.УстановитьЗначение("Ссылка", СтрокаТекущегоПрофиля.Ссылка);
		НачатьТранзакцию();
		Попытка
			Блокировка.Заблокировать();
			ПрофильОбъект = СтрокаТекущегоПрофиля.Ссылка.ПолучитьОбъект();
			Если Не ПрофильОбъект.ПометкаУдаления Тогда
				ПрофильОбъект.ПометкаУдаления = Истина;
				ОбновлениеИнформационнойБазы.ЗаписатьОбъект(ПрофильОбъект);
				Если ОбновленныеПрофили <> Неопределено Тогда
					ОбновленныеПрофили.Добавить(ПрофильОбъект.Ссылка);
				КонецЕсли;
				ЕстьИзменения = Истина;
			КонецЕсли;
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ОбновитьПоставляемыеПрофили.
Процедура ОбновитьПоставляемыеПрофилиБезПапок(ОбновленныеПрофили, ТекущиеПрофили,
			ТекущиеПапкиПрофилей, ПоставляемыеПрофили, Удаленные, ЕстьИзменения)
	
	ОписанияПрофилей    = ПоставляемыеПрофили.ОписанияПрофилейМассив;
	ПараметрыОбновления = ПоставляемыеПрофили.ПараметрыОбновления;
	
	Для Каждого СвойстваПрофиля Из ОписанияПрофилей Цикл
		СвойстваПрофиля = СвойстваПрофиля; // см. Справочники.ПрофилиГруппДоступа.СвойстваПоставляемогоПрофиля
		Если СвойстваПрофиля.ЭтоГруппа Тогда
			Продолжить;
		КонецЕсли;
		
		Идентификатор = Новый УникальныйИдентификатор(СвойстваПрофиля.Идентификатор);
		СтрокаТекущегоПрофиля = ТекущиеПрофили.Найти(Идентификатор, "ИдентификаторПоставляемыхДанных");
		
		ПрофильОбновлен = Ложь;
		
		Если СтрокаТекущегоПрофиля = Неопределено Тогда
			// Создание нового поставляемого профиля.
			// @skip-check query-in-loop - Порционная обработка данных в транзакции
			Если ОбновитьПрофильИлиПапкуПрофилей(СвойстваПрофиля, Удаленные, Истина) Тогда
				ЕстьИзменения = Истина;
			КонецЕсли;
			// @skip-check query-in-loop - Порционная обработка данных в транзакции
			Профиль = ПоставляемыйПрофильПоИдентификатору(СвойстваПрофиля.Идентификатор);
			
		Иначе
			СтрокаТекущегоПрофиля.Найден = Истина;
			
			Профиль = СтрокаТекущегоПрофиля.Ссылка;
			Если Не СтрокаТекущегоПрофиля.ПоставляемыйПрофильИзменен
			 Или ПараметрыОбновления.ОбновлятьИзмененныеПрофили Тогда
				// Обновление поставляемого профиля.
				// @skip-check query-in-loop - Порционная обработка данных в транзакции
				ПрофильОбновлен = ОбновитьПрофильИлиПапкуПрофилей(СвойстваПрофиля, Удаленные, Истина);
			КонецЕсли;
		КонецЕсли;
		
		Если ПараметрыОбновления.ОбновлятьГруппыДоступа Тогда
			ГруппыДоступаПрофиляОбновлены = Справочники.ГруппыДоступа.ОбновитьГруппыДоступаПрофиля(
				Профиль, ПараметрыОбновления.ОбновлятьГруппыДоступаСУстаревшимиНастройками);
			
			ПрофильОбновлен = ПрофильОбновлен Или ГруппыДоступаПрофиляОбновлены;
		КонецЕсли;
		
		Если ПрофильОбновлен Тогда
			ЕстьИзменения = Истина;
			ОбновленныеПрофили.Добавить(Профиль);
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедур ОбновитьПоставляемыеПапкиПрофилей, ОбновитьПоставляемыеПрофилиБезПапок и
// ЗаполнитьПоставляемыйПрофиль.
//
// Замещает имеющийся или создает новый поставляемый профиль групп доступа по его описанию.
//
// Параметры:
//  СвойстваПрофиля - см. СвойстваПоставляемогоПрофиля
//  Удаленные - Массив
//            - Неопределено
//  НеОбновлятьРолиПользователей - Булево
//
// Возвращаемое значение:
//  Булево -  Истина - профиль изменен.
//
Функция ОбновитьПрофильИлиПапкуПрофилей(СвойстваПрофиля, Удаленные = Неопределено, НеОбновлятьРолиПользователей = Ложь)
	
	ПрофильИзменен = Ложь;
	УдаленПредопределенныйЭлементПапкиИлиПрофиля = ТипЗнч(Удаленные) = Тип("Массив")
		И Удаленные.Найти(СвойстваПрофиля.Имя) <> Неопределено;
	
	ПрофильСсылка = ПоставляемыйПрофильПоИдентификатору(СвойстваПрофиля.Идентификатор);
	Если ПрофильСсылка = Неопределено Тогда
		
		Если ЗначениеЗаполнено(СвойстваПрофиля.Имя) Тогда
			Запрос = Новый Запрос;
			Запрос.Текст =
			"ВЫБРАТЬ
			|	ПрофилиГруппДоступа.Ссылка КАК Ссылка,
			|	ПрофилиГруппДоступа.ИмяПредопределенныхДанных КАК ИмяПредопределенныхДанных
			|ИЗ
			|	Справочник.ПрофилиГруппДоступа КАК ПрофилиГруппДоступа
			|ГДЕ
			|	ПрофилиГруппДоступа.Предопределенный = ИСТИНА
			|	И ПрофилиГруппДоступа.ЭтоГруппа = &ЭтоГруппа";
			Запрос.УстановитьПараметр("ЭтоГруппа", СвойстваПрофиля.ЭтоГруппа);
			Выборка = Запрос.Выполнить().Выбрать();
			
			Пока Выборка.Следующий() Цикл
				ИмяПредопределенного = Выборка.ИмяПредопределенныхДанных;
				Если ВРег(СвойстваПрофиля.Имя) = ВРег(ИмяПредопределенного) Тогда
					ПрофильСсылка = Выборка.Ссылка;
					Прервать;
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		
		Если ПрофильСсылка = Неопределено Тогда
			Запрос = Новый Запрос;
			Запрос.Текст =
			"ВЫБРАТЬ
			|	ПрофилиГруппДоступа.Ссылка КАК Ссылка
			|ИЗ
			|	Справочник.ПрофилиГруппДоступа КАК ПрофилиГруппДоступа
			|ГДЕ
			|	ПрофилиГруппДоступа.Предопределенный = ЛОЖЬ
			|	И ПрофилиГруппДоступа.ЭтоГруппа = &ЭтоГруппа
			|	И ПрофилиГруппДоступа.Ссылка = &Ссылка";
			Запрос.УстановитьПараметр("ЭтоГруппа", СвойстваПрофиля.ЭтоГруппа);
			Запрос.УстановитьПараметр("Ссылка",
				ПолучитьСсылку(Новый УникальныйИдентификатор(СвойстваПрофиля.Идентификатор)));
			
			Выборка = Запрос.Выполнить().Выбрать();
			Если Выборка.Следующий() Тогда
				ПрофильСсылка = Выборка.Ссылка;
			КонецЕсли;
		КонецЕсли;
		
		Если ПрофильСсылка = Неопределено
		   И СвойстваПрофиля.ЭтоГруппа
		   И ЗначениеЗаполнено(СвойстваПрофиля.Наименование) Тогда
			
			Запрос = Новый Запрос;
			Запрос.Текст =
			"ВЫБРАТЬ
			|	ПрофилиГруппДоступа.Ссылка КАК Ссылка
			|ИЗ
			|	Справочник.ПрофилиГруппДоступа КАК ПрофилиГруппДоступа
			|ГДЕ
			|	ПрофилиГруппДоступа.Предопределенный = ЛОЖЬ
			|	И ПрофилиГруппДоступа.ЭтоГруппа
			|	И ПрофилиГруппДоступа.Наименование = &Наименование";
			Запрос.УстановитьПараметр("Наименование", СвойстваПрофиля.Наименование);
			
			Выборка = Запрос.Выполнить().Выбрать();
			Если Выборка.Следующий() Тогда
				ПрофильСсылка = Выборка.Ссылка;
			КонецЕсли;
		КонецЕсли;
		
		Если ПрофильСсылка = Неопределено Тогда
			// Поставляемый элемент не существует, нужно создать новый.
			ПрофильОбъект = ?(СвойстваПрофиля.ЭтоГруппа, СоздатьГруппу(), СоздатьЭлемент());
		Иначе
			// Поставляемый элемент связан с предопределенным элементом.
			ПрофильОбъект = ПрофильСсылка.ПолучитьОбъект();
		КонецЕсли;
		
		ПрофильОбъект.ИдентификаторПоставляемыхДанных =
			Новый УникальныйИдентификатор(СвойстваПрофиля.Идентификатор);
		
		ПрофильИзменен = Истина;
	Иначе
		ПрофильОбъект = ПрофильСсылка.ПолучитьОбъект();
		ПрофильИзменен = ПоставляемыйПрофильИзменен(ПрофильОбъект)
			Или УдаленПредопределенныйЭлементПапкиИлиПрофиля;
	КонецЕсли;
	
	Если ПрофильИзменен Тогда
		
		Если Не ОбновлениеИнформационнойБазы.ВыполняетсяОбновлениеИнформационнойБазы()
		   И Не ОбновлениеИнформационнойБазы.ЭтоВызовИзОбработчикаОбновления() Тогда
			
			ЗаблокироватьДанныеДляРедактирования(ПрофильОбъект.Ссылка, ПрофильОбъект.ВерсияДанных);
		КонецЕсли;
		
		Если УдаленПредопределенныйЭлементПапкиИлиПрофиля Тогда
			ПрофильОбъект.ПометкаУдаления = Ложь;
		КонецЕсли;
		
		ПрофильОбъект.Наименование = СвойстваПрофиля.Наименование;
		
		Если ЗначениеЗаполнено(СвойстваПрофиля.Родитель) Тогда
			Родитель = ПоставляемыйПрофильПоИдентификатору(СвойстваПрофиля.Родитель);
			Если ЗначениеЗаполнено(Родитель) Тогда
				ПрофильОбъект.Родитель = Родитель;
			КонецЕсли;
		КонецЕсли;
		
		Если Не СвойстваПрофиля.ЭтоГруппа Тогда
			ПрофильОбъект.Роли.Очистить();
			Для каждого Роль Из ОписаниеРолейПрофиля(СвойстваПрофиля) Цикл
				МетаданныеРоли = Метаданные.Роли.Найти(Роль);
				Если МетаданныеРоли = Неопределено Тогда
					ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'При обновлении поставляемого профиля ""%1""
						           |обнаружена несуществующая роль ""%2"".'"),
						СвойстваПрофиля.Наименование,
						Роль);
					ВызватьИсключение ТекстОшибки;
				КонецЕсли;
				ПрофильОбъект.Роли.Добавить().Роль =
					ОбщегоНазначения.ИдентификаторОбъектаМетаданных(МетаданныеРоли);
			КонецЦикла;
			
			ПрофильОбъект.ВидыДоступа.Очистить();
			Для каждого ОписаниеВидаДоступа Из СвойстваПрофиля.ВидыДоступа Цикл
				СвойстваВидаДоступа = УправлениеДоступомСлужебный.СвойстваВидаДоступа(ОписаниеВидаДоступа.Ключ);
				Строка = ПрофильОбъект.ВидыДоступа.Добавить();
				Строка.ВидДоступа        = СвойстваВидаДоступа.Ссылка;
				Строка.Предустановленный = ОписаниеВидаДоступа.Значение = "Предустановленный";
				Строка.ВсеРазрешены      = ОписаниеВидаДоступа.Значение = "ВначалеВсеРазрешены";
			КонецЦикла;
			
			ПрофильОбъект.ЗначенияДоступа.Очистить();
			Для каждого ОписаниеЗначенияДоступа Из СвойстваПрофиля.ЗначенияДоступа Цикл
				СвойстваВидаДоступа = УправлениеДоступомСлужебный.СвойстваВидаДоступа(ОписаниеЗначенияДоступа.ВидДоступа);
				СтрокаЗначения = ПрофильОбъект.ЗначенияДоступа.Добавить();
				СтрокаЗначения.ВидДоступа = СвойстваВидаДоступа.Ссылка;
				СтрокаЗначения.ЗначениеДоступа = ПредопределенноеЗначение(ОписаниеЗначенияДоступа.ЗначениеДоступа);
			КонецЦикла;
			
			ПрофильОбъект.Назначение.Очистить();
			Для каждого ТипНазначения Из СвойстваПрофиля.Назначение Цикл
				СтрокаНазначения = ПрофильОбъект.Назначение.Добавить();
				СтрокаНазначения.ТипПользователей = ТипНазначения;
			КонецЦикла;
			
			Если НеОбновлятьРолиПользователей Тогда
				ПрофильОбъект.ДополнительныеСвойства.Вставить("НеОбновлятьРолиПользователей");
			КонецЕсли;
		КонецЕсли;
		
		Если Не Справочники.ВерсииРасширений.ВсеРасширенияПодключены() Тогда
			СтарыеЗначения = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(ПрофильОбъект.Ссылка, "ВидыДоступа, ЗначенияДоступа");
			ВосстановитьНесуществующиеВидыИЗначенияДоступа(СтарыеЗначения, ПрофильОбъект);
		КонецЕсли;
		
		ОбновлениеИнформационнойБазы.ЗаписатьОбъект(ПрофильОбъект);
		
		Если Не ОбновлениеИнформационнойБазы.ВыполняетсяОбновлениеИнформационнойБазы()
		   И Не ОбновлениеИнформационнойБазы.ЭтоВызовИзОбработчикаОбновления() Тогда
			
			РазблокироватьДанныеДляРедактирования(ПрофильОбъект.Ссылка);
		КонецЕсли;
		
	КонецЕсли;
	
	Возврат ПрофильИзменен;
	
КонецФункции

// Для функций ПоставляемыйПрофильИзменен и ОбновитьПрофильГруппДоступа.
Функция ОписаниеРолейПрофиля(ОписаниеПрофиля)
	
	НазначениеПрофиля = УправлениеДоступомСлужебныйКлиентСервер.НазначениеПрофиля(ОписаниеПрофиля);
	НедоступныеРоли = ПользователиСлужебныйПовтИсп.НедоступныеРоли(НазначениеПрофиля);
	
	ОписаниеРолейПрофиля = Новый Массив;
	
	Для Каждого Роль Из ОписаниеПрофиля.Роли Цикл
		Если НедоступныеРоли.Получить(Роль) = Неопределено Тогда
			ОписаниеРолейПрофиля.Добавить(Роль);
		КонецЕсли;
	КонецЦикла;
	
	ЗаполнитьСтандартныеРолиРасширений(ОписаниеРолейПрофиля);
	
	Возврат Новый ФиксированныйМассив(ОписаниеРолейПрофиля);
	
КонецФункции

// Для процедуры ПодготовитьРолиПоставляемогоПрофиля.
Функция РолиПрофиляНедоступныеВСервисе(ОписаниеПрофиля, НазначениеПрофиля)
	
	НедоступныеРоли = ПользователиСлужебныйПовтИсп.НедоступныеРоли(НазначениеПрофиля, Истина);
	НедоступныеРолиПрофиля = Новый Соответствие;
	
	Для Каждого Роль Из ОписаниеПрофиля.Роли Цикл
		Если НедоступныеРоли.Получить(Роль) <> Неопределено Тогда
			НедоступныеРолиПрофиля.Вставить(Роль, Истина);
		КонецЕсли;
	КонецЦикла;
	
	Возврат Новый ФиксированноеСоответствие(НедоступныеРолиПрофиля);
	
КонецФункции

// Для процедуры ОбновитьПоставляемыеПрофили.
//
// Возвращаемое значение:
//  ТаблицаЗначений:
//     * ИдентификаторПоставляемыхДанных - УникальныйИдентификатор
//     * Ссылка - СправочникСсылка.ПрофилиГруппДоступа
//     * ЭтоГруппа - Булево
//
Функция ТекущиеПапкиПрофилей()
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ПустойУникальныйИдентификатор",
		ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор());
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ПрофилиГруппДоступа.ИдентификаторПоставляемыхДанных КАК ИдентификаторПоставляемыхДанных,
	|	ПрофилиГруппДоступа.Ссылка КАК Ссылка,
	|	ПрофилиГруппДоступа.ЭтоГруппа КАК ЭтоГруппа,
	|	ЛОЖЬ КАК Найден
	|ИЗ
	|	Справочник.ПрофилиГруппДоступа КАК ПрофилиГруппДоступа
	|ГДЕ
	|	ПрофилиГруппДоступа.ЭтоГруппа
	|	И ПрофилиГруппДоступа.ИдентификаторПоставляемыхДанных <> &ПустойУникальныйИдентификатор";
	ТекущиеПрофили = Запрос.Выполнить().Выгрузить();
	
	Возврат ТекущиеПрофили;
	
КонецФункции

// Для процедуры ОбновитьПоставляемыеПрофили.
//
// Возвращаемое значение:
//  ТаблицаЗначений:
//     * ПоставляемыйПрофильИзменен - Булево
//     * ИдентификаторПоставляемыхДанных - УникальныйИдентификатор
//     * Ссылка - СправочникСсылка.ПрофилиГруппДоступа
//     * ЭтоГруппа - Булево
//
Функция ТекущиеПрофили()
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ПустойУникальныйИдентификатор",
		ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор());
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ПрофилиГруппДоступа.ПоставляемыйПрофильИзменен КАК ПоставляемыйПрофильИзменен,
	|	ПрофилиГруппДоступа.ИдентификаторПоставляемыхДанных КАК ИдентификаторПоставляемыхДанных,
	|	ПрофилиГруппДоступа.ЭтоГруппа КАК ЭтоГруппа,
	|	ПрофилиГруппДоступа.Ссылка КАК Ссылка,
	|	ЛОЖЬ КАК Найден
	|ИЗ
	|	Справочник.ПрофилиГруппДоступа КАК ПрофилиГруппДоступа
	|ГДЕ
	|	НЕ ПрофилиГруппДоступа.ЭтоГруппа
	|	И ПрофилиГруппДоступа.ИдентификаторПоставляемыхДанных <> &ПустойУникальныйИдентификатор";
	ТекущиеПрофили = Запрос.Выполнить().Выгрузить();
	
	Возврат ТекущиеПрофили;
	
КонецФункции

Функция ОсновныеНепоставляемыеПрофили()
	
	ПоставляемыеПрофили = УправлениеДоступомСлужебный.ПоставляемыеПрофили();
	ИдентификаторыПоставляемыхПрофилей = Новый Массив;
	
	Для Каждого ОписаниеПрофиля Из ПоставляемыеПрофили.ОписанияПрофилейМассив Цикл
		Если ОписаниеПрофиля.ЭтоГруппа Тогда
			Продолжить;
		КонецЕсли;
		ИдентификаторыПоставляемыхПрофилей.Добавить(
			Новый УникальныйИдентификатор(ОписаниеПрофиля.Идентификатор));
	КонецЦикла;
	
	РолиОсновныхПрофилей = Новый Массив;
	РолиОсновныхПрофилей.Добавить(ОбщегоНазначения.ИдентификаторОбъектаМетаданных(
		Метаданные.Роли.ПолныеПрава.ПолноеИмя()));
	РолиОсновныхПрофилей.Добавить(ОбщегоНазначения.ИдентификаторОбъектаМетаданных(
		Метаданные.Роли.БазовыеПраваБСП.ПолноеИмя()));
	РолиОсновныхПрофилей.Добавить(ОбщегоНазначения.ИдентификаторОбъектаМетаданных(
		Метаданные.Роли.БазовыеПраваВнешнихПользователейБСП.ПолноеИмя()));
	РолиОсновныхПрофилей.Добавить(ОбщегоНазначения.ИдентификаторОбъектаМетаданных(
		Метаданные.Роли.АдминистраторСистемы.ПолноеИмя()));
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ИдентификаторыПоставляемыхПрофилей", ИдентификаторыПоставляемыхПрофилей);
	Запрос.УстановитьПараметр("РолиОсновныхПрофилей", РолиОсновныхПрофилей);
	Запрос.Текст =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	ПрофилиГруппДоступа.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.ПрофилиГруппДоступа КАК ПрофилиГруппДоступа
	|ГДЕ
	|	НЕ ПрофилиГруппДоступа.ЭтоГруппа
	|	И НЕ ПрофилиГруппДоступа.ИдентификаторПоставляемыхДанных В (&ИдентификаторыПоставляемыхПрофилей)
	|	И ИСТИНА В
	|			(ВЫБРАТЬ ПЕРВЫЕ 1
	|				ИСТИНА
	|			ИЗ
	|				Справочник.ПрофилиГруппДоступа.Роли КАК РолиПрофилей
	|			ГДЕ
	|				РолиПрофилей.Ссылка = ПрофилиГруппДоступа.Ссылка
	|				И РолиПрофилей.Роль В (&РолиОсновныхПрофилей))";
	
	Возврат Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
	
КонецФункции

// Для функции НесовместимыеПрофилиГруппДоступа.
//
// Возвращаемое значение:
//  ТаблицаЗначений:
//    * Ссылка - СправочникСсылка.ПрофилиГруппДоступа
//    * Назначение - ТаблицаЗначений:
//        ** ТипПользователей - ОпределяемыйТип.Пользователь
//    * Роли - ТаблицаЗначений:
//        ** Роль - СправочникСсылка.ИдентификаторыОбъектовРасширений
//                - СправочникСсылка.ИдентификаторыОбъектовМетаданных
//
Функция НазначениеИРолиПрофилейГруппаДоступа()
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Профили.Ссылка КАК Ссылка,
	|	Профили.Назначение.(
	|		ТипПользователей КАК ТипПользователей
	|	) КАК Назначение,
	|	Профили.Роли.(
	|		Роль КАК Роль
	|	) КАК Роли
	|ИЗ
	|	Справочник.ПрофилиГруппДоступа КАК Профили";
	
	Возврат Запрос.Выполнить().Выгрузить();
	
КонецФункции

#КонецОбласти

#КонецЕсли
